Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/django 42 compatible #2

Open
wants to merge 21 commits into
base: release/4.0.1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [ 3.7, 3.8, 3.9 ] # latest release minus two
python-version: [ 3.8, 3.9, '3.10' ] # latest release minus two
requirements-file: [
django-2.2.txt,
django-3.0.txt,
django-3.1.txt,
django-3.2.txt,
django-4.2.txt
]
os: [
ubuntu-20.04,
Expand Down Expand Up @@ -59,20 +57,18 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [ 3.7, 3.8, 3.9 ] # latest release minus two
python-version: [ 3.8, 3.9, '3.10' ] # latest release minus two
requirements-file: [
django-2.2.txt,
django-3.0.txt,
django-3.1.txt,
django-3.2.txt,
django-4.2.txt,
]
os: [
ubuntu-20.04,
]

services:
mysql:
image: mysql:5.7
image: mysql:8.0
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: djangocms_test
Expand Down Expand Up @@ -109,12 +105,10 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [ 3.7, 3.8, 3.9 ] # latest release minus two
python-version: [ 3.8, 3.9, '3.10' ] # latest release minus two
requirements-file: [
django-2.2.txt,
django-3.0.txt,
django-3.1.txt,
django-3.2.txt,
django-4.2.txt,
]
os: [
ubuntu-20.04,
Expand Down
20 changes: 20 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2

build:
os: ubuntu-22.04
tools:
python: "3.9"

sphinx:
configuration: docs/conf.py
fail_on_warning: false

formats:
- epub
- pdf

python:
install:
- requirements: docs/requirements.txt
4 changes: 4 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
=== 4.0.1 (unreleased) ===
* Introduced Django 4.2 support.
* Dropped Support for Django<3.1


=== 4.0.0 (unreleased) ===
* feat: Added live-url querystring parameter option for PageContent edit and preview endpoints
Expand Down
7 changes: 2 additions & 5 deletions cms/admin/pageadmin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from collections import namedtuple
import copy
import json

import django
Expand Down Expand Up @@ -88,6 +87,7 @@ def get_site(request):
return site


@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
change_list_template = "admin/cms/page/tree/base.html"
actions_menu_template = 'admin/cms/page/tree/actions_dropdown.html'
Expand Down Expand Up @@ -780,6 +780,7 @@ class Meta:
return render(request, 'admin/cms/page/plugin/change_form.html', context)


@admin.register(PageContent)
class PageContentAdmin(admin.ModelAdmin):
ordering = ('page__node__path',)
search_fields = ('=id', 'page__id', 'page__urls__slug', 'title', 'page__reverse_id')
Expand Down Expand Up @@ -905,8 +906,6 @@ def duplicate(self, request, object_id):
if obj is None:
raise self._get_404_exception(object_id)

request = copy.copy(request)

if request.method == 'GET':
# source is a field in the form
# because its value is in the url,
Expand Down Expand Up @@ -1477,5 +1476,3 @@ def render_page_row(page):
yield render_page_row(page)


admin.site.register(Page, PageAdmin)
admin.site.register(PageContent, PageContentAdmin)
2 changes: 1 addition & 1 deletion cms/admin/placeholderadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class PlaceholderAdminMixin(metaclass=PlaceholderAdminMixinBase):
pass


@admin.register(Placeholder)
class PlaceholderAdmin(admin.ModelAdmin):

def has_add_permission(self, request):
Expand Down Expand Up @@ -1093,4 +1094,3 @@ def clear_placeholder(self, request, placeholder_id):
return TemplateResponse(request, "admin/cms/page/plugin/delete_confirmation.html", context)


admin.site.register(Placeholder, PlaceholderAdmin)
12 changes: 6 additions & 6 deletions cms/admin/settingsadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from django.db import transaction
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseBadRequest
from django.http.request import QueryDict
from django.urls import re_path
from django.urls import path
from django.utils.html import conditional_escape
from django.utils.translation import override

Expand All @@ -21,6 +21,7 @@
from cms.utils.urlutils import admin_reverse


@admin.register(UserSettings)
class SettingsAdmin(ModelAdmin):

def get_urls(self):
Expand All @@ -33,16 +34,16 @@ def wrapper(*args, **kwargs):
info = self.model._meta.app_label, self.model._meta.model_name

return [
re_path(r'^session_store/$',
path('session_store/',
self.session_store,
name='%s_%s_session_store' % info),
re_path(r'^cms-toolbar/$',
path('cms-toolbar/',
wrap(self.get_toolbar),
name='%s_%s_get_toolbar' % info),
re_path(r'^$',
path('',
wrap(self.change_view),
name='%s_%s_change' % info),
re_path(r'^(.+)/$',
path('<path:id>/',
wrap(self.change_view),
name='%s_%s_change' % info),
]
Expand Down Expand Up @@ -137,4 +138,3 @@ def get_model_perms(self, request):
return {}


admin.site.register(UserSettings, SettingsAdmin)
2 changes: 1 addition & 1 deletion cms/admin/static_placeholder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from django.contrib import admin


@admin.register(StaticPlaceholder)
class StaticPlaceholderAdmin(admin.ModelAdmin):
list_display = ('get_name', 'code', 'site', 'creation_method')
search_fields = ('name', 'code',)
exclude = ('creation_method',)
list_filter = ('creation_method', 'site')

admin.site.register(StaticPlaceholder, StaticPlaceholderAdmin)
2 changes: 1 addition & 1 deletion cms/cms_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from logging import getLogger
from collections import Iterable
from collections.abc import Iterable

from django.core.exceptions import ImproperlyConfigured

Expand Down
4 changes: 2 additions & 2 deletions cms/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from cms.utils.urlutils import admin_reverse
from django.http import HttpResponseForbidden, HttpResponseBadRequest, HttpResponse
from django.middleware.csrf import get_token
from django.urls import re_path
from django.urls import path
from django.utils.translation import gettext, gettext_lazy as _, get_language


Expand Down Expand Up @@ -65,7 +65,7 @@ def get_extra_placeholder_menu_items(cls, request, placeholder):

def get_plugin_urls(self):
return [
re_path(r'^create_alias/$', self.create_alias, name='cms_create_alias'),
path('create_alias/', self.create_alias, name='cms_create_alias'),
]

@classmethod
Expand Down
73 changes: 57 additions & 16 deletions cms/middleware/language.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,63 @@
import datetime

from django.utils.translation import LANGUAGE_SESSION_KEY, get_language
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
from django.utils.translation import get_language

from cms.utils.compat import DJANGO_2_2

if DJANGO_2_2:
from django.utils.translation import LANGUAGE_SESSION_KEY


class LanguageCookieMiddleware(MiddlewareMixin):
def process_response(self, request, response):
language = get_language()
if hasattr(request, 'session'):
session_language = request.session.get(LANGUAGE_SESSION_KEY, None)
if session_language and not session_language == language:
request.session[LANGUAGE_SESSION_KEY] = language
request.session.save()
if settings.LANGUAGE_COOKIE_NAME in request.COOKIES and \
request.COOKIES[settings.LANGUAGE_COOKIE_NAME] == language:
def __init__(self, get_response):
super().__init__(get_response)

if DJANGO_2_2:

def __call__(self, request):
response = self.get_response(request)
language = get_language()
if hasattr(request, 'session'):
session_language = request.session.get(LANGUAGE_SESSION_KEY, None)
if session_language and not session_language == language:
request.session[LANGUAGE_SESSION_KEY] = language
request.session.save()
if (
settings.LANGUAGE_COOKIE_NAME in request.COOKIES
and request.COOKIES[settings.LANGUAGE_COOKIE_NAME] == language # noqa: W503
):
return response
response.set_cookie(
settings.LANGUAGE_COOKIE_NAME,
value=language,
domain=settings.LANGUAGE_COOKIE_DOMAIN,
max_age=settings.LANGUAGE_COOKIE_AGE or 365 * 24 * 60 * 60, # 1 year
path=settings.LANGUAGE_COOKIE_PATH,
)
return response
else:

def __call__(self, request):
response = self.get_response(request)
language = get_language()
if (
settings.LANGUAGE_COOKIE_NAME in request.COOKIES # noqa: W503
and request.COOKIES[settings.LANGUAGE_COOKIE_NAME] == language
):
return response

# To ensure support of very old browsers, Django processed automatically "expires" according
# to max_age value.
# https://docs.djangoproject.com/en/3.2/ref/request-response/#django.http.HttpResponse.set_cookie

response.set_cookie(
settings.LANGUAGE_COOKIE_NAME,
value=language,
domain=settings.LANGUAGE_COOKIE_DOMAIN,
max_age=settings.LANGUAGE_COOKIE_AGE or 365 * 24 * 60 * 60, # 1 year
httponly=settings.LANGUAGE_COOKIE_HTTPONLY,
path=settings.LANGUAGE_COOKIE_PATH,
samesite=settings.LANGUAGE_COOKIE_SAMESITE,
secure=settings.LANGUAGE_COOKIE_SECURE,
)
return response
max_age = 365 * 24 * 60 * 60 # 10 years
expires = datetime.datetime.utcnow() + datetime.timedelta(seconds=max_age)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language, expires=expires)
return response
42 changes: 6 additions & 36 deletions cms/signals/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,52 +13,22 @@
#################### Our own signals ###################

# fired after page location is changed - is moved from one node to other
page_moved = Signal(providing_args=["instance"])
page_moved = Signal()

# fired if a public page with an apphook is added or changed
urls_need_reloading = Signal(providing_args=[])
urls_need_reloading = Signal()

# *disclaimer*
# The generic object operation signals are very likely to change
# as their usage evolves.
# As a result, rely on these at your own risk
pre_obj_operation = Signal(
providing_args=[
"operation",
"request",
"token",
"obj",
]
)
pre_obj_operation = Signal()

post_obj_operation = Signal(
providing_args=[
"operation",
"request",
"token",
"obj",
]
)
post_obj_operation = Signal()

pre_placeholder_operation = Signal(
providing_args=[
"operation",
"request",
"language",
"token",
"origin",
]
)
pre_placeholder_operation = Signal()

post_placeholder_operation = Signal(
providing_args=[
"operation",
"request",
"language",
"token",
"origin",
]
)
post_placeholder_operation = Signal()


################### apphook reloading ###################
Expand Down
2 changes: 1 addition & 1 deletion cms/templatetags/cms_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def _get_page_by_untyped_arg(page_lookup, request, site_id):
middle = BrokenLinkEmailsMiddleware()
domain = request.get_host()
path = request.get_full_path()
referer = force_str(request.META.get('HTTP_REFERER', ''), errors='replace')
referer = force_str(request.headers.get('referer', ''), errors='replace')
if not middle.is_ignorable_request(request, path, domain, referer):
mail_managers(subject, body, fail_silently=True)
return None
Expand Down
3 changes: 2 additions & 1 deletion cms/test_utils/project/cms_urls_for_apphook_tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from cms.apphook_pool import apphook_pool
from cms.views import details
from django.conf import settings
from django.urls import path
from django.urls import re_path


Expand All @@ -11,7 +12,7 @@

urlpatterns = [
# Public pages
re_path(r'^$', details, {'slug':''}, name='pages-root'),
path('', details, {'slug':''}, name='pages-root'),
reg,
]

Expand Down
4 changes: 2 additions & 2 deletions cms/test_utils/project/customuserapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from django.utils.http import urlquote
from urllib.parse import quote
from django.utils.translation import gettext_lazy as _


Expand Down Expand Up @@ -60,7 +60,7 @@ class Meta:
verbose_name_plural = _('users')

def get_absolute_url(self):
return "/users/%s/" % urlquote(self.username)
return "/users/%s/" % quote(self.username)

def get_full_name(self):
"""
Expand Down
Loading
Loading