django-cms-qe 4.1.0__py3-none-any.whl → 4.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cms_qe/cms4_menus.py ADDED
@@ -0,0 +1,132 @@
1
+ from cms.cms_menus import get_menu_node_for_page, get_visible_nodes
2
+ from cms.models import EmptyPageContent, PageContent, PageUrl
3
+ from cms.toolbar.utils import get_toolbar_from_request
4
+ from cms.utils.i18n import get_fallback_languages, get_public_languages, hide_untranslated, is_valid_site_language
5
+ from cms.utils.page import get_page_queryset
6
+ from django.db.models.query import Prefetch, prefetch_related_objects
7
+ from menus.base import Menu
8
+
9
+ from .cms_menus_mixin import CMSMenuMixin
10
+
11
+
12
+ # This class i a copy of https://github.com/django-cms/django-cms/blob/4.1.7/cms/cms_menus.py#L199
13
+ # with extra function custom_menu_node.
14
+ class CMSMenu(CMSMenuMixin, Menu):
15
+ """Subclass of :class:`menus.base.Menu`. Its :meth:`~menus.base.Menu.get_nodes()` creates
16
+ a list of NavigationNodes based on a site's :class:`cms.models.pagemodel.Page` objects.
17
+ """
18
+
19
+ def get_nodes(self, request):
20
+ site = self.renderer.site
21
+ lang = self.renderer.request_language
22
+ toolbar = get_toolbar_from_request(request)
23
+
24
+ pages = get_page_queryset(site)
25
+
26
+ if is_valid_site_language(lang, site_id=site.pk):
27
+ _valid_language = True
28
+ _hide_untranslated = hide_untranslated(lang, site.pk)
29
+ else:
30
+ _valid_language = False
31
+ _hide_untranslated = False
32
+
33
+ if _valid_language:
34
+ # The request language has been explicitly configured
35
+ # for the current site.
36
+ if _hide_untranslated:
37
+ fallbacks = []
38
+ else:
39
+ fallbacks = get_fallback_languages(lang, site_id=site.pk)
40
+ languages = [lang] + [_lang for _lang in fallbacks if _lang != lang]
41
+ else:
42
+ # The request language is not configured for the current site.
43
+ # Fallback to all configured public languages for the current site.
44
+ languages = get_public_languages(site.pk)
45
+ fallbacks = languages
46
+
47
+ pages = (
48
+ pages.filter(pagecontent_set__language__in=languages)
49
+ .select_related("node")
50
+ .order_by("node__path")
51
+ .distinct()
52
+ )
53
+ pages = get_visible_nodes(request, pages, site)
54
+
55
+ if not pages:
56
+ return []
57
+
58
+ try:
59
+ homepage = [page for page in pages if page.is_home][0]
60
+ except IndexError:
61
+ homepage = None
62
+
63
+ urls_lookup = Prefetch(
64
+ "urls",
65
+ to_attr="filtered_urls",
66
+ queryset=PageUrl.objects.filter(language__in=languages),
67
+ )
68
+ if toolbar.edit_mode_active or toolbar.preview_mode_active:
69
+ # Get all translations visible in the admin for the current page
70
+ translations_qs = PageContent.admin_manager.current_content(language__in=languages)
71
+ else:
72
+ # Only get public translations
73
+ translations_qs = PageContent.objects.filter(language__in=languages)
74
+ translations_lookup = Prefetch(
75
+ "pagecontent_set",
76
+ to_attr="filtered_translations",
77
+ queryset=translations_qs,
78
+ )
79
+ prefetch_related_objects(pages, urls_lookup, translations_lookup)
80
+ # Build the blank title instances only once
81
+ blank_page_content_cache = {language: EmptyPageContent(language=language) for language in languages}
82
+
83
+ # Maps a node id to its page id
84
+ node_id_to_page: dict[int, int] = {}
85
+
86
+ def _page_to_node(page):
87
+ # EmptyPageContent is used to prevent the cms from trying
88
+ # to find a translation in the database
89
+ page.page_content_cache = blank_page_content_cache.copy()
90
+
91
+ for page_url in page.filtered_urls:
92
+ page.urls_cache[page_url.language] = page_url
93
+
94
+ for trans in page.filtered_translations:
95
+ page.page_content_cache[trans.language] = trans
96
+
97
+ menu_node = get_menu_node_for_page(
98
+ self.renderer,
99
+ page,
100
+ language=lang,
101
+ fallbacks=fallbacks,
102
+ endpoint=toolbar.preview_mode_active or toolbar.edit_mode_active,
103
+ )
104
+ return menu_node
105
+
106
+ menu_nodes = []
107
+
108
+ for page in pages:
109
+ node = page.node
110
+ parent_id = node_id_to_page.get(node.parent_id)
111
+
112
+ if node.parent_id and not parent_id:
113
+ # If the parent page is not available (unpublished, etc..)
114
+ # don't bother creating menu nodes for its descendants.
115
+ continue
116
+
117
+ menu_node = _page_to_node(page)
118
+ if menu_node:
119
+ # Only add pages with at least one page content
120
+ cut_homepage = homepage and not homepage.get_in_navigation(lang)
121
+
122
+ if cut_homepage and parent_id == homepage.pk:
123
+ # When the homepage is hidden from navigation,
124
+ # we need to cut all its direct children from it.
125
+ menu_node.parent_id = None
126
+ else:
127
+ menu_node.parent_id = parent_id
128
+ # This is the reason for the class copy. This line is added.
129
+ self.extend_node_attr(menu_node.attr, page)
130
+ node_id_to_page[node.pk] = page.pk
131
+ menu_nodes.append(menu_node)
132
+ return menu_nodes
cms_qe/cms5_menus.py ADDED
@@ -0,0 +1,87 @@
1
+ import re
2
+ from typing import Optional
3
+
4
+ from cms.cms_menus import (VISIBLE_FOR_ANONYMOUS, VISIBLE_FOR_AUTHENTICATED, CMSMenu as CMS5Menu, CMSNavigationNode,
5
+ apphook_pool)
6
+ from cms.models import PageContent
7
+
8
+ from .cms_menus_mixin import CMSMenuMixin
9
+
10
+
11
+ class CMSMenu(CMSMenuMixin, CMS5Menu):
12
+
13
+ # This class i a copy of https://github.com/django-cms/django-cms/blob/5.0.4/cms/cms_menus.py#L199
14
+ # see line above comment: This line is added.
15
+ def get_menu_node_for_page_content(
16
+ self,
17
+ page_content: PageContent,
18
+ preview_url: Optional[str] = None,
19
+ cut: bool = False,
20
+ ) -> CMSNavigationNode:
21
+ """
22
+ Transform a CMS page content object into a navigation node.
23
+
24
+ :param page: The page to transform.
25
+ :param languages: The list of the current language plus fallbacks used to render the menu.
26
+ :param preview_url: If given, serves as a "pattern" for a preview url with the assumption that "/0/"
27
+ is replaced by the actual page content pk. Default is None.
28
+ :param cut: If True the parent_id is set to None. Default is False.
29
+ :returns: A CMSNavigationNode instance.
30
+ """
31
+ page = page_content.page
32
+
33
+ # These are simple to port over, since they are not calculated.
34
+ # Other attributes will be added conditionally later.
35
+ visibility = page_content.limit_visibility_in_menu
36
+ attr = {
37
+ "is_page": True,
38
+ "soft_root": page_content.soft_root,
39
+ "auth_required": page.login_required,
40
+ "reverse_id": page.reverse_id,
41
+ "is_home": page.is_home,
42
+ "visible_for_authenticated": visibility in VISIBLE_FOR_AUTHENTICATED,
43
+ "visible_for_anonymous": visibility in VISIBLE_FOR_ANONYMOUS,
44
+ }
45
+
46
+ extenders = []
47
+ if page.navigation_extenders:
48
+ if page.navigation_extenders in self.renderer.menus:
49
+ extenders.append(page.navigation_extenders)
50
+ elif f"{page.navigation_extenders}:{page.pk}" in self.renderer.menus:
51
+ extenders.append(f"{page.navigation_extenders}:{page.pk}")
52
+ # Is this page an apphook? If so, we need to handle the apphooks's nodes
53
+ # Only run this if we have a translation in the requested language for this
54
+ # object. The page content cache should have been prepopulated in CMSMenu.get_nodes
55
+ # but otherwise, just request the title normally
56
+ if page.application_urls and page_content.language == self.languages[0]:
57
+ # it means it is an apphook
58
+ app = apphook_pool.get_apphook(page.application_urls)
59
+ if app:
60
+ extenders.extend(app.get_menus(page, self.languages[0]))
61
+ # CMSAattachMenus are treated a bit differently to allow them to be
62
+ # able to be attached to multiple points in the navigation.
63
+ attr["navigation_extenders"] = [
64
+ f"{ext.__name__}:{page.pk}" if hasattr(ext, "get_instances") else getattr(ext, "__name__", ext)
65
+ for ext in extenders
66
+ ]
67
+ # This is the reason for the class copy. This line is added.
68
+ self.extend_node_attr(attr, page)
69
+
70
+ # Now finally, build the NavigationNode object and return it.
71
+ # The parent_id is manually set by the menu get_nodes method.
72
+ if preview_url:
73
+ # Build preview url by replacing "/0/" in the url template by the actual pk of the page content object
74
+ # Hacky, but faster than calling `admin_reverse` for each page content object
75
+ url = re.sub("(/0/)", f"/{page_content.pk}/", preview_url)
76
+ else:
77
+ url = page.get_absolute_url(language=page_content.language)
78
+
79
+ return CMSNavigationNode(
80
+ title=page_content.menu_title or page_content.title,
81
+ url=url,
82
+ id=page.pk,
83
+ parent_id=None if cut else page_content.page.parent_id,
84
+ attr=attr,
85
+ visible=page_content.in_navigation,
86
+ language=(page_content.language if page_content.language != self.languages[0] else None),
87
+ )
cms_qe/cms_menus.py CHANGED
@@ -1,134 +1,7 @@
1
- from cms.cms_menus import CMSNavigationNode, get_menu_node_for_page, get_visible_nodes
2
- from cms.models import EmptyPageContent, Page, PageContent, PageUrl
3
- from cms.toolbar.utils import get_toolbar_from_request
4
- from cms.utils.i18n import get_fallback_languages, get_public_languages, hide_untranslated, is_valid_site_language
5
- from cms.utils.page import get_page_queryset
6
- from django.db.models.query import Prefetch, prefetch_related_objects
7
- from django.http import HttpRequest
8
- from menus.base import Menu
1
+ from cms import __version__
9
2
 
10
-
11
- # This class i a copy of https://github.com/django-cms/django-cms/blob/4.1.7/cms/cms_menus.py#L199
12
- # with extra function custom_menu_node.
13
- class CMSMenu(Menu):
14
- """Subclass of :class:`menus.base.Menu`. Its :meth:`~menus.base.Menu.get_nodes()` creates a list of NavigationNodes
15
- based on a site's :class:`cms.models.pagemodel.Page` objects.
16
- """
17
-
18
- def get_nodes(self, request):
19
- site = self.renderer.site
20
- lang = self.renderer.request_language
21
- toolbar = get_toolbar_from_request(request)
22
-
23
- pages = get_page_queryset(site)
24
-
25
- if is_valid_site_language(lang, site_id=site.pk):
26
- _valid_language = True
27
- _hide_untranslated = hide_untranslated(lang, site.pk)
28
- else:
29
- _valid_language = False
30
- _hide_untranslated = False
31
-
32
- if _valid_language:
33
- # The request language has been explicitly configured
34
- # for the current site.
35
- if _hide_untranslated:
36
- fallbacks = []
37
- else:
38
- fallbacks = get_fallback_languages(lang, site_id=site.pk)
39
- languages = [lang] + [_lang for _lang in fallbacks if _lang != lang]
40
- else:
41
- # The request language is not configured for the current site.
42
- # Fallback to all configured public languages for the current site.
43
- languages = get_public_languages(site.pk)
44
- fallbacks = languages
45
-
46
- pages = (
47
- pages.filter(pagecontent_set__language__in=languages)
48
- .select_related("node")
49
- .order_by("node__path")
50
- .distinct()
51
- )
52
- pages = get_visible_nodes(request, pages, site)
53
-
54
- if not pages:
55
- return []
56
-
57
- try:
58
- homepage = [page for page in pages if page.is_home][0]
59
- except IndexError:
60
- homepage = None
61
-
62
- urls_lookup = Prefetch(
63
- "urls",
64
- to_attr="filtered_urls",
65
- queryset=PageUrl.objects.filter(language__in=languages),
66
- )
67
- if toolbar.edit_mode_active or toolbar.preview_mode_active:
68
- # Get all translations visible in the admin for the current page
69
- translations_qs = PageContent.admin_manager.current_content(language__in=languages)
70
- else:
71
- # Only get public translations
72
- translations_qs = PageContent.objects.filter(language__in=languages)
73
- translations_lookup = Prefetch(
74
- "pagecontent_set",
75
- to_attr="filtered_translations",
76
- queryset=translations_qs,
77
- )
78
- prefetch_related_objects(pages, urls_lookup, translations_lookup)
79
- # Build the blank title instances only once
80
- blank_page_content_cache = {language: EmptyPageContent(language=language) for language in languages}
81
-
82
- # Maps a node id to its page id
83
- node_id_to_page: dict[int, int] = {}
84
-
85
- def _page_to_node(page):
86
- # EmptyPageContent is used to prevent the cms from trying
87
- # to find a translation in the database
88
- page.page_content_cache = blank_page_content_cache.copy()
89
-
90
- for page_url in page.filtered_urls:
91
- page.urls_cache[page_url.language] = page_url
92
-
93
- for trans in page.filtered_translations:
94
- page.page_content_cache[trans.language] = trans
95
-
96
- menu_node = get_menu_node_for_page(
97
- self.renderer,
98
- page,
99
- language=lang,
100
- fallbacks=fallbacks,
101
- endpoint=toolbar.preview_mode_active or toolbar.edit_mode_active,
102
- )
103
- return menu_node
104
-
105
- menu_nodes = []
106
-
107
- for page in pages:
108
- node = page.node
109
- parent_id = node_id_to_page.get(node.parent_id)
110
-
111
- if node.parent_id and not parent_id:
112
- # If the parent page is not available (unpublished, etc..)
113
- # don't bother creating menu nodes for its descendants.
114
- continue
115
-
116
- menu_node = _page_to_node(page)
117
- if menu_node:
118
- # Only add pages with at least one page content
119
- cut_homepage = homepage and not homepage.get_in_navigation(lang)
120
-
121
- if cut_homepage and parent_id == homepage.pk:
122
- # When the homepage is hidden from navigation,
123
- # we need to cut all its direct children from it.
124
- menu_node.parent_id = None
125
- else:
126
- menu_node.parent_id = parent_id
127
- menu_node = self.custom_menu_node(request, page, menu_node)
128
- node_id_to_page[node.pk] = page.pk
129
- menu_nodes.append(menu_node)
130
- return menu_nodes
131
-
132
- def custom_menu_node(self, request: HttpRequest, page: Page, menu_node: CMSNavigationNode) -> CMSNavigationNode:
133
- menu_node.attr["page_description"] = page.get_meta_description()
134
- return menu_node
3
+ # pylint: skip-file
4
+ if __version__.split(".")[0] == "4":
5
+ from .cms4_menus import CMSMenu # noqa: F401
6
+ else:
7
+ from .cms5_menus import CMSMenu # noqa: F401
@@ -0,0 +1,10 @@
1
+ from typing import Any
2
+
3
+ from cms.models import Page
4
+
5
+
6
+ class CMSMenuMixin:
7
+
8
+ def extend_node_attr(self, attr: dict[str, Any], page: Page) -> None:
9
+ """Extend node attrs."""
10
+ attr["page_description"] = page.get_meta_description()
@@ -0,0 +1,12 @@
1
+ {% load menu_tags %}
2
+
3
+ {% for child in children %}
4
+ <li class="child{% if child.selected %} selected{% endif %}{% if child.ancestor %} ancestor{% endif %}{% if child.sibling %} sibling{% endif %}{% if child.descendant %} descendant{% endif %}">
5
+ <a href="{{ child.attr.redirect_url|default:child.get_absolute_url }}"{% if child.attr.page_description %} title="{{ child.attr.page_description }}"{% endif %}>{{ child.get_menu_title }}</a>
6
+ {% if child.children %}
7
+ <ul>
8
+ {% show_menu from_level to_level extra_inactive extra_active template "" "" child %}
9
+ </ul>
10
+ {% endif %}
11
+ </li>
12
+ {% endfor %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cms-qe
3
- Version: 4.1.0
3
+ Version: 4.2.0
4
4
  Summary: Django CMS Quick & Easy provides all important modules to run new page withouta lot of coding. Aims to do it very easily and securely.
5
5
  Home-page: https://websites.pages.nic.cz/django-cms-qe
6
6
  Author: CZ.NIC, z.s.p.o.
@@ -26,15 +26,15 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
26
26
  Requires-Python: >=3.10
27
27
  Description-Content-Type: text/markdown
28
28
  License-File: LICENSE
29
- Requires-Dist: django-cms~=4.1
29
+ Requires-Dist: django-cms<6,>=4.1
30
30
  Requires-Dist: django-filer~=3.3
31
31
  Requires-Dist: djangocms-admin-style~=3.3
32
- Requires-Dist: djangocms-alias~=2.0
33
- Requires-Dist: djangocms-frontend~=2.1
34
- Requires-Dist: djangocms-text~=0.8
35
- Requires-Dist: djangocms-versioning~=2.3
32
+ Requires-Dist: djangocms-alias<4,>=2.0
33
+ Requires-Dist: djangocms-frontend~=2.2
34
+ Requires-Dist: djangocms-text~=0.9
35
+ Requires-Dist: djangocms-versioning~=2.4
36
36
  Requires-Dist: easy-thumbnails[svg]~=2.10
37
- Requires-Dist: argon2-cffi~=23.1
37
+ Requires-Dist: argon2-cffi~=25.1
38
38
  Requires-Dist: django-axes~=8.0
39
39
  Requires-Dist: django-constance~=4.3
40
40
  Requires-Dist: django-csp~=4.0
@@ -43,40 +43,40 @@ Requires-Dist: django-tablib~=3.2
43
43
  Requires-Dist: djangocms-file~=3.0
44
44
  Requires-Dist: djangocms-googlemap~=2.2
45
45
  Requires-Dist: djangocms-icon~=2.1
46
- Requires-Dist: djangocms-link~=5.0
46
+ Requires-Dist: djangocms-link~=5.1
47
47
  Requires-Dist: djangocms-picture~=4.1
48
48
  Requires-Dist: python-environ~=0.4
49
49
  Requires-Dist: django-haystack~=3.3
50
- Requires-Dist: djangocms-aldryn-forms[captcha]~=8.0
51
- Requires-Dist: djangocms-aldryn-search~=3.0
50
+ Requires-Dist: djangocms-aldryn-forms[captcha]~=8.3
51
+ Requires-Dist: djangocms-aldryn-search~=3.1
52
52
  Requires-Dist: mailchimp3~=3.0
53
53
  Requires-Dist: whoosh~=2.7
54
- Requires-Dist: Markdown~=3.8
55
- Requires-Dist: django-filter~=25.1
54
+ Requires-Dist: Markdown~=3.9
55
+ Requires-Dist: django-filter~=25.2
56
56
  Requires-Dist: django-rest-knox~=5.0
57
57
  Requires-Dist: djangorestframework~=3.16
58
58
  Requires-Dist: drf-spectacular~=0.28
59
59
  Provides-Extra: dev
60
- Requires-Dist: django-simple-captcha~=0.5; extra == "dev"
61
- Requires-Dist: django-debug-toolbar~=4.1; extra == "dev"
62
- Requires-Dist: django-extensions~=3.2; extra == "dev"
60
+ Requires-Dist: django-simple-captcha~=0.6; extra == "dev"
61
+ Requires-Dist: django-debug-toolbar~=6.0; extra == "dev"
62
+ Requires-Dist: django-extensions~=4.1; extra == "dev"
63
63
  Provides-Extra: test
64
64
  Requires-Dist: flake8; extra == "test"
65
65
  Requires-Dist: isort; extra == "test"
66
66
  Requires-Dist: mypy; extra == "test"
67
67
  Requires-Dist: pylint; extra == "test"
68
68
  Requires-Dist: pylint-django; extra == "test"
69
- Requires-Dist: pytest~=6.2; extra == "test"
70
- Requires-Dist: pytest-cov~=6.2; extra == "test"
69
+ Requires-Dist: pytest~=8.4; extra == "test"
70
+ Requires-Dist: pytest-cov~=7.0; extra == "test"
71
71
  Requires-Dist: pytest-data~=0.4; extra == "test"
72
- Requires-Dist: pytest-django~=3.9; extra == "test"
73
- Requires-Dist: pytest-env~=0.6; extra == "test"
72
+ Requires-Dist: pytest-django~=4.11; extra == "test"
73
+ Requires-Dist: pytest-env~=1.2; extra == "test"
74
74
  Requires-Dist: pytest-pythonpath~=0.7; extra == "test"
75
- Requires-Dist: pytest-sugar~=0.9; extra == "test"
75
+ Requires-Dist: pytest-sugar~=1.1; extra == "test"
76
76
  Requires-Dist: pytest-watch~=4.2; extra == "test"
77
- Requires-Dist: PyVirtualDisplay~=1.3; extra == "test"
77
+ Requires-Dist: PyVirtualDisplay~=3.0; extra == "test"
78
78
  Requires-Dist: webdriverwrapper~=2.8; extra == "test"
79
- Requires-Dist: django-simple-captcha~=0.5; extra == "test"
79
+ Requires-Dist: django-simple-captcha~=0.6; extra == "test"
80
80
  Requires-Dist: testfixtures; extra == "test"
81
81
  Requires-Dist: tzdata; extra == "test"
82
82
  Provides-Extra: build
@@ -88,6 +88,8 @@ Provides-Extra: mysql
88
88
  Requires-Dist: mysqlclient~=2.2; extra == "mysql"
89
89
  Provides-Extra: newsblog
90
90
  Requires-Dist: djangocms-aldryn-newsblog~=4.0; extra == "newsblog"
91
+ Provides-Extra: cms4
92
+ Requires-Dist: django-cms~=4.1; extra == "cms4"
91
93
  Dynamic: author
92
94
  Dynamic: author-email
93
95
  Dynamic: classifier
@@ -1,7 +1,10 @@
1
1
  cms_qe/__init__.py,sha256=do1c4s8BgjukMZMMMhBHs_lG9j8ncnAjR3oTICWEq5w,684
2
2
  cms_qe/admin.py,sha256=eLqAF3UIDWWyA0xE0Ft5WP5_3HImSWk3EYL2cRfL_8A,171
3
3
  cms_qe/apps.py,sha256=AeRcBWwGs7rKLlzHhnV8M_2BEnkoO9959VwesxfHaio,338
4
- cms_qe/cms_menus.py,sha256=KdgZgvzRZwkpqviv3exizo_Vf5qtJziC7Q2yFRslJSQ,5470
4
+ cms_qe/cms4_menus.py,sha256=gfiA1EMoVzl2ZtDIZ0xIwFCotT7c-H7jswVkSZbtooo,5311
5
+ cms_qe/cms5_menus.py,sha256=m1U3ez7CLLSneQLTTy6S0dTylZeLVoqRd_HZOBgiCqo,4124
6
+ cms_qe/cms_menus.py,sha256=D9sG4BhVsQzTOkVL0_-3JHBQpWoMq66g-IWW-DbDKgE,192
7
+ cms_qe/cms_menus_mixin.py,sha256=1i0t2PQoabzffuOZKIhneiJJ9K8Nz70dDS6DqWlJMRg,245
5
8
  cms_qe/constants.py,sha256=YWUWCIabSwcamGZynvkJ9i8OWGtfHf-wFirm8GtqQpI,90
6
9
  cms_qe/export.py,sha256=3MflO_EmaCrlqqa-cTMOiRirsp3r4mxdQNt-Zh5FtzY,8242
7
10
  cms_qe/fixtures.py,sha256=cq_wnZnqBwPBOHpp_0bHk424iCXKvwmN6ZaKwDvguXk,755
@@ -90,6 +93,7 @@ cms_qe/templates/cms_qe/include/search_form.html,sha256=Wo1AZMWmdE9dgvdWlUZtmZ0V
90
93
  cms_qe/templates/cms_qe/tests/test_email.txt,sha256=i0XFIXcYFTx2IaXQaaRLd6lK8Mv2Gd0OxVrwKt8uUBQ,14
91
94
  cms_qe/templates/cmsplugin_filer_folder/plugins/folder/gallery.html,sha256=TKCWjgBLZZOZtPvg3EAcg8sXta2rj4y6aJpvlA7eZag,246
92
95
  cms_qe/templates/cmsplugin_filer_folder/plugins/folder/main.html,sha256=8IiTXNU4K4hKQL6vwqRBbK7TzpeNxEEopq9NG6RZO0A,166
96
+ cms_qe/templates/menu/menu.html,sha256=ZZIhe6acgxwiF9Bgb8yH7XKpI6zpQ0RCDdG8nmJTL1E,592
93
97
  cms_qe/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
98
  cms_qe/templatetags/cms_qe_filters.py,sha256=ciYCXLuMVde-f_-B_7a5mHfAJPWPMxYEUMgCHpualRY,784
95
99
  cms_qe/templatetags/kwacros.py,sha256=r2oHLltu8BgKKlrpgzXgvLjbIqwcrH13Lww3zTF-nr8,6275
@@ -3970,21 +3974,8 @@ cms_qe_video/templates/cms_qe/video/video_source_file.html,sha256=QJF5fs88s9Fznp
3970
3974
  cms_qe_video/templates/cms_qe/video/video_widget.html,sha256=Yumciq6bGlAYI1lYx5j9V6IF8QYrncNYygPTkXEz6Wk,925
3971
3975
  cms_qe_video/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3972
3976
  cms_qe_video/templatetags/cms_qe_video.py,sha256=NR_mGv91J0rEreZrQjCzaaXSrZsKvrSas12wMJ-Dg24,1168
3973
- django_cms_qe-4.1.0.dist-info/licenses/LICENSE,sha256=5wLaeUil0gfU9p8C4zn2Yu_PvZBNieUoYl0z9FcFWdA,1521
3974
- test_selenium/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3975
- test_selenium/browser.py,sha256=OcfqxDa9OtL7M5CSwfIxtzToMUEhqGLvditemPeEUNo,1437
3976
- test_selenium/conftest.py,sha256=mAptaAyj7a1hbUPDRWBBs1qL0TJ8Fma7Mch6PZwgtNo,220
3977
- test_selenium/cms_qe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3978
- test_selenium/cms_qe/test_base.py,sha256=RbJQLvSvrskOibwY7CmR2XrYCWHBk2UTVyvJwZuDXpE,36
3979
- test_selenium/cms_qe/test_errors.py,sha256=oGwegQ6hmS-VgnuYKRFOSaVpfNXX5tDdNemGSJoDNpw,378
3980
- test_selenium/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3981
- test_selenium/fixtures/base.py,sha256=c5oqqqvtCRUmVQdvlu6kPBypGz8gdIPJP0Gcvl6fBGs,1788
3982
- test_selenium/pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3983
- test_selenium/pages/cms/__init__.py,sha256=_qe4YZYaQbrXp7Szmmeo4TUSkXlE5Rozu8E3tthA6-E,150
3984
- test_selenium/pages/cms/login.py,sha256=UPzJQcYff8NUAT4nvmfQoJQxzOJyPrJ_cKtH35NVfNg,521
3985
- test_selenium/pages/cms/page.py,sha256=YQnpZkopfVnhoyQKpRDGqjNeV6xUl-pEHjEcZ9HRiPk,489
3986
- test_selenium/pages/cms/wizard.py,sha256=yatbXH-rf1ap4O1hY0I13WikM3zkm_NrAiSK6bqENIU,545
3987
- django_cms_qe-4.1.0.dist-info/METADATA,sha256=Jh_kHri1S4t76H1AAFtTMjvzx56EFXELL2HgwBEtzNI,6234
3988
- django_cms_qe-4.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3989
- django_cms_qe-4.1.0.dist-info/top_level.txt,sha256=fQYSfQoprw1NXhYY-I8AzsPk-Rgst1REh3iOUvwAbkM,164
3990
- django_cms_qe-4.1.0.dist-info/RECORD,,
3977
+ django_cms_qe-4.2.0.dist-info/licenses/LICENSE,sha256=5wLaeUil0gfU9p8C4zn2Yu_PvZBNieUoYl0z9FcFWdA,1521
3978
+ django_cms_qe-4.2.0.dist-info/METADATA,sha256=8KG2Og6RSiKpsypY_x35P3vKw4FdNsEznKd-XaXsyKE,6310
3979
+ django_cms_qe-4.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3980
+ django_cms_qe-4.2.0.dist-info/top_level.txt,sha256=u9pqfoJfXwh29tMO7lnphA5yn7ChYZquFe7_sNtEiW8,150
3981
+ django_cms_qe-4.2.0.dist-info/RECORD,,
@@ -9,4 +9,3 @@ cms_qe_plugins
9
9
  cms_qe_table
10
10
  cms_qe_test
11
11
  cms_qe_video
12
- test_selenium
test_selenium/__init__.py DELETED
File without changes
test_selenium/browser.py DELETED
@@ -1,47 +0,0 @@
1
- from selenium.common.exceptions import NoSuchElementException
2
- from webdriverwrapper import Chrome as _Chrome
3
-
4
-
5
- class ChromeBrowser(_Chrome):
6
- def get_error_page(self):
7
- # Django error page.
8
- try:
9
- error_page = self.get_elm('summary')
10
- except NoSuchElementException:
11
- pass
12
- else:
13
- header = error_page.get_elm(tag_name='h1')
14
- return header.text
15
-
16
- # Generic error page.
17
- try:
18
- error_page = self.get_elm('error-page')
19
- except NoSuchElementException:
20
- pass
21
- else:
22
- header = error_page.get_elm(tag_name='h1')
23
- return header.text
24
-
25
- def get_error_traceback(self):
26
- try:
27
- traceback = self.get_elm('traceback_area')
28
- except NoSuchElementException:
29
- pass
30
- else:
31
- return traceback.text
32
-
33
- def get_error_messages(self):
34
- try:
35
- error_elms = self.get_elms(xpath='//*[contains(@class, "alert-danger")]')
36
- except NoSuchElementException:
37
- return []
38
- else:
39
- return [error_elm.text for error_elm in error_elms]
40
-
41
- def get_info_messages(self):
42
- try:
43
- info_elms = self.get_elms(xpath='//*[contains(@class, "alert-success")]')
44
- except NoSuchElementException:
45
- return []
46
- else:
47
- return [info_elm.get_attribute('info') for info_elm in info_elms]
File without changes
@@ -1,2 +0,0 @@
1
- def test_homepage(driver):
2
- pass
@@ -1,14 +0,0 @@
1
- from webdriverwrapper.decorators import expected_error_page
2
-
3
- from ..pages.cms import CreatePagePage
4
-
5
-
6
- @expected_error_page(None)
7
- def test_page_not_found(driver):
8
- driver.go_to('/404')
9
-
10
-
11
- # No error page should be detected.
12
- def test_page_not_found_custom_by_cms(driver):
13
- CreatePagePage(driver).open().create_page('Page not found', slug='error404')
14
- driver.go_to('/404')
test_selenium/conftest.py DELETED
@@ -1,6 +0,0 @@
1
- def pytest_addoption(parser):
2
- parser.addoption('--no-display', action='store_true', help='do not use virtual display')
3
-
4
-
5
- def pytest_configure(config):
6
- config.webdriverwrapper_screenshot_path = '/tmp/testresults'
File without changes
@@ -1,75 +0,0 @@
1
- import os
2
-
3
- import pytest
4
- from selenium import webdriver
5
- from webdriverwrapper.pytest import * # noqa: F403,F401
6
-
7
- from ..browser import ChromeBrowser
8
- from ..pages.cms import LoginPage, WizardPage
9
-
10
- __all__ = [
11
- 'display',
12
- 'session_driver',
13
- '_driver',
14
- 'homepage_url',
15
- 'admin_username',
16
- ]
17
-
18
-
19
- @pytest.fixture(scope='session', autouse=True)
20
- def display(request):
21
- no_display = request.config.getoption('--no-display')
22
-
23
- if no_display:
24
- yield
25
- else:
26
- from pyvirtualdisplay import Display
27
-
28
- display = Display(visible=0, size=(1200, 2000))
29
- display.start()
30
- yield
31
- display.stop()
32
-
33
-
34
- @pytest.yield_fixture(scope='session')
35
- def session_driver(request, homepage_url, admin_username):
36
- no_display = request.config.getoption('--no-display')
37
-
38
- driver = open_browser(homepage_url)
39
- try:
40
- LoginPage(driver).open().login(admin_username)
41
- WizardPage(driver).open().create_home_page()
42
- yield driver
43
- finally:
44
- if not no_display:
45
- driver.quit()
46
-
47
-
48
- def open_browser(homepage_url):
49
- """
50
- Open browser a type URL `homepage_url`.
51
- """
52
- chrome_options = webdriver.ChromeOptions()
53
- chrome_options.add_argument('--incognito')
54
- chrome_options.add_argument('--no-sandbox') # So it will work in GitLab CI.
55
-
56
- driver = ChromeBrowser(chrome_options=chrome_options)
57
- driver.get(homepage_url)
58
- return driver
59
-
60
-
61
- @pytest.fixture
62
- def _driver(session_driver, homepage_url):
63
- session_driver.get(homepage_url)
64
- return session_driver
65
-
66
-
67
- @pytest.fixture(scope='session')
68
- def homepage_url():
69
- port = os.environ.get('WEB_PORT', '8000')
70
- return f'http://localhost:{port}'
71
-
72
-
73
- @pytest.fixture(scope='session')
74
- def admin_username():
75
- return os.environ.get('ADMIN_USER', 'admin')
File without changes
@@ -1,5 +0,0 @@
1
- from .login import LoginPage
2
- from .page import CreatePagePage
3
- from .wizard import WizardPage
4
-
5
- __all__ = ['LoginPage', 'CreatePagePage', 'WizardPage']
@@ -1,17 +0,0 @@
1
- class LoginPage:
2
- def __init__(self, driver):
3
- self._driver = driver
4
-
5
- def open(self):
6
- self._driver.go_to('/admin')
7
- return self
8
-
9
- def login(self, username, password=None):
10
- """
11
- Log in to Django CMS application as `username` with `password`.
12
- If `password` is empty, then is used same as `username`.
13
- """
14
- self._driver.get_elm('login-form').fill_out_and_submit({
15
- 'username': username,
16
- 'password': password or username,
17
- })
@@ -1,16 +0,0 @@
1
- class CreatePagePage:
2
- def __init__(self, driver):
3
- self._driver = driver
4
-
5
- def open(self):
6
- self._driver.go_to('/admin/cms/page/add/')
7
- return self
8
-
9
- def create_page(self, title, slug=''):
10
- # Title will automatically set slug which we want to force.
11
- self._driver.get_elm(tag_name='form').fill_out({
12
- 'title': title,
13
- })
14
- self._driver.get_elm(tag_name='form').fill_out_and_submit({
15
- 'slug': slug,
16
- })
@@ -1,17 +0,0 @@
1
- class WizardPage:
2
- def __init__(self, driver):
3
- self._driver = driver
4
-
5
- def open(self):
6
- self._driver.go_to('/cms_wizard/create/?language=en')
7
- return self
8
-
9
- def create_home_page(self):
10
- """
11
- Creates empty homepage for Django CMS. Usuful to create some
12
- at the beggining of tests so any test can start somewhere.
13
- """
14
- self._driver.get_elm(tag_name='form').submit()
15
- self._driver.get_elm(tag_name='form').fill_out_and_submit({
16
- '1-title': 'homepage',
17
- })