django-cms-qe 3.1.0__py3-none-any.whl → 3.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/apps.py ADDED
@@ -0,0 +1,12 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class CmsQEConfig(AppConfig):
5
+ name = 'cms_qe'
6
+
7
+ def ready(self):
8
+ from cms.signals import urls_need_reloading # pylint: disable=import-outside-toplevel
9
+
10
+ from .signals import reload_site # pylint: disable=import-outside-toplevel
11
+
12
+ urls_need_reloading.connect(reload_site)
@@ -131,3 +131,6 @@ MIDDLEWARE = [
131
131
  # Must be the last.
132
132
  'django.middleware.cache.FetchFromCacheMiddleware',
133
133
  ]
134
+
135
+ # Reload site. For example ['uwsgi', '--reload', '/var/run/uwsgi.pid'] or ['touch', 'manage.py'].
136
+ RELOAD_SITE: list[str] = []
cms_qe/signals.py ADDED
@@ -0,0 +1,42 @@
1
+ import logging
2
+ import subprocess
3
+ import time
4
+ from multiprocessing import Lock, Process
5
+ from typing import Any, Optional, Union
6
+
7
+ from django.conf import settings
8
+ from django.db.models import Model
9
+
10
+ DelayType = Union[int, float]
11
+ ArgsType = Union[tuple[str, ...], list[str]]
12
+
13
+ logger = logging.getLogger(__name__)
14
+ lock = Lock()
15
+
16
+
17
+ def run_subprocess(args: ArgsType, delay: Optional[DelayType] = None) -> Optional[subprocess.CompletedProcess]:
18
+ """Run the system command in subprocess."""
19
+ if delay is not None:
20
+ time.sleep(delay)
21
+ logger.info('subprocess.run(%s)', args)
22
+ lock.acquire()
23
+ try:
24
+ return subprocess.run(args, check=False)
25
+ except Exception as err: # pylint: disable=broad-exception-caught
26
+ logger.error(str(err))
27
+ finally:
28
+ lock.release()
29
+ return None
30
+
31
+
32
+ def run_process(args: ArgsType, delay: Optional[DelayType] = None) -> Process:
33
+ """Run the process and no wait for the result."""
34
+ proc = Process(target=run_subprocess, args=(args, delay))
35
+ proc.start()
36
+ return proc
37
+
38
+
39
+ def reload_site(sender: Optional[Model], **kwargs: dict[str, Any]) -> None: # pylint: disable=unused-argument
40
+ "Reload the site."
41
+ if settings.RELOAD_SITE:
42
+ run_process(settings.RELOAD_SITE, 3)
cms_qe/urls.py CHANGED
@@ -13,6 +13,7 @@ from django.contrib.sitemaps.views import sitemap
13
13
  from django.urls import include, path
14
14
  from django.views.i18n import JavaScriptCatalog
15
15
 
16
+ from cms_qe.views.maintenance import HealthCheckView, ReloadSiteView
16
17
  from cms_qe.views.search_result import SiteSearchView
17
18
  from cms_qe.views.security import SecurityTxtView
18
19
 
@@ -41,6 +42,8 @@ urlpatterns = [
41
42
  path('api/monitoring', views.get_monitoring),
42
43
  path('.well-known/security.txt', SecurityTxtView.as_view(), name='security-txt'),
43
44
  path('site-search-result/', SiteSearchView.as_view(), name='site-search-result'),
45
+ path("healthcheck/", HealthCheckView.as_view(), name='healthcheck'), # Used by uwsgi in docker.
46
+ path("superuser/reload-site/", ReloadSiteView.as_view(), name='reload-site'),
44
47
  ]
45
48
 
46
49
  # django-simple-captcha
@@ -0,0 +1,39 @@
1
+ from django.conf import settings
2
+ from django.contrib.sites.models import Site
3
+ from django.core.cache import cache
4
+ from django.http import HttpRequest, HttpResponse
5
+ from django.views import View
6
+
7
+ from cms_qe.signals import run_subprocess
8
+
9
+
10
+ class HealthCheckView(View):
11
+ """Health check View."""
12
+
13
+ def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
14
+ """Apply HTTP GET. Used by uwsgi in docker."""
15
+ # Check database connection.
16
+ site = Site.objects.first()
17
+ if not site.domain:
18
+ raise RuntimeError('db')
19
+ # Check cache.
20
+ key = 'health-check-test'
21
+ cache.set(key, 'OK', 2)
22
+ if cache.get(key) != 'OK':
23
+ raise RuntimeError('cache')
24
+ return HttpResponse("OK", content_type="text/plain")
25
+
26
+
27
+ class ReloadSiteView(View):
28
+ """Reload site View."""
29
+
30
+ def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
31
+ """Run subprocess defined in settings."""
32
+ if request.user.is_superuser:
33
+ if settings.RELOAD_SITE:
34
+ msg = str(run_subprocess(settings.RELOAD_SITE))
35
+ else:
36
+ msg = "SKIP: No settings.RELOAD_SITE."
37
+ else:
38
+ msg = "ERROR: User is not is_superuser."
39
+ return HttpResponse(msg, content_type="text/plain")
@@ -0,0 +1,11 @@
1
+ {% if config.GOOGLE_TAG_MANAGER_ID %}
2
+ <!-- Google tag (gtag.js) -->
3
+ <script async src="https://www.googletagmanager.com/gtag/js?id={{ config.GOOGLE_TAG_MANAGER_ID }}"></script>
4
+ <script>
5
+ window.dataLayer = window.dataLayer || [];
6
+ function gtag() {dataLayer.push(arguments)}
7
+ gtag('js', new Date());
8
+ gtag('config', '{{ config.GOOGLE_TAG_MANAGER_ID }}');
9
+ {{ config.GOOGLE_TAG_MANAGER_SCRIPT|safe }}
10
+ </script>
11
+ {% endif %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-cms-qe
3
- Version: 3.1.0
3
+ Version: 3.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.
@@ -73,6 +73,7 @@ Requires-Dist: pytest-watch ==4.2.0 ; extra == 'test'
73
73
  Requires-Dist: PyVirtualDisplay ==1.3.2 ; extra == 'test'
74
74
  Requires-Dist: webdriverwrapper ==2.8.0 ; extra == 'test'
75
75
  Requires-Dist: django-simple-captcha ==0.5.14 ; extra == 'test'
76
+ Requires-Dist: testfixtures ; extra == 'test'
76
77
  Requires-Dist: tzdata ; extra == 'test'
77
78
 
78
79
  # Django CMS QE
@@ -1,11 +1,13 @@
1
1
  cms_qe/__init__.py,sha256=do1c4s8BgjukMZMMMhBHs_lG9j8ncnAjR3oTICWEq5w,684
2
2
  cms_qe/admin.py,sha256=eLqAF3UIDWWyA0xE0Ft5WP5_3HImSWk3EYL2cRfL_8A,171
3
+ cms_qe/apps.py,sha256=AeRcBWwGs7rKLlzHhnV8M_2BEnkoO9959VwesxfHaio,338
3
4
  cms_qe/constants.py,sha256=YWUWCIabSwcamGZynvkJ9i8OWGtfHf-wFirm8GtqQpI,90
4
5
  cms_qe/export.py,sha256=PyKzMoFGzMdwCTKIYP1zri0Pn8zR_2e0-IkbHYfAv3g,8138
5
6
  cms_qe/fixtures.py,sha256=cq_wnZnqBwPBOHpp_0bHk424iCXKvwmN6ZaKwDvguXk,755
6
7
  cms_qe/monitoring.py,sha256=5t_o7o0htmAAxVjkN2oz0O0v9XdzfePhSfPGcLNPmE8,769
8
+ cms_qe/signals.py,sha256=MbuLSxPlJA147LEg-lDWDoUNTV1y0OKjwoI3HzgR97g,1253
7
9
  cms_qe/staticfiles.py,sha256=OHkfDfpIxN0B-eCRagZzHDHyBgaulcyYgKhp_3mPZuk,1363
8
- cms_qe/urls.py,sha256=DoOO3xijoxLpdfJAfl4DNna0q0nwG-OKGOdhzpWV1Ss,2790
10
+ cms_qe/urls.py,sha256=vNiiN7NwFFT48SBNIg6lzjTRMN6BicwH_zz6p-EApsw,3042
9
11
  cms_qe/utils.py,sha256=gxsWZmS34ZC0Tv1VW8A7VeGlrPyDshodF1ZWXj7xyWE,3057
10
12
  cms_qe/boilerplates/bootstrap3/static/cms_qe/css/bootstrap-theme.css,sha256=xOpS-e_dER8z72w-qryCieOGysQI8cELAVt3MHG0phY,26132
11
13
  cms_qe/boilerplates/bootstrap3/static/cms_qe/css/bootstrap-theme.css.map,sha256=cZQbJTuJQjdM1XuKhdRzSJ8hMRQ4up491P76Iq2_D1M,47706
@@ -45,7 +47,7 @@ cms_qe/settings/__init__.py,sha256=GJwHXMHwMuGYE-3ZzePJ-26I2WwE8bAIMUDoiTFr0L8,9
45
47
  cms_qe/settings/dev.py,sha256=51CBwiclE8LLoNB2uioIK_L3JhM1yzukQ0gZimkcFqw,1487
46
48
  cms_qe/settings/unittest.py,sha256=folLIMJb1Arh60_Sn0eNQrvIlx0OsAs6v1tDfyRZVuQ,514
47
49
  cms_qe/settings/base/__init__.py,sha256=5yHfne9gPD_xuTaG3voZP23yzuCwROmif2mmKs-hG_A,446
48
- cms_qe/settings/base/app.py,sha256=vzHEMzjghEuBrF0iALbvV5KE3rq4lZ8CVoaY-I5JXnA,3839
50
+ cms_qe/settings/base/app.py,sha256=vAq5Eo2LK6yU19cvbLrpLpWLV_REhdVRs9NfCZGEeu8,3966
49
51
  cms_qe/settings/base/auth.py,sha256=axEmEG5UYxyY3EE0keQjGZzkTv2iwxwfULr4LcvZPns,207
50
52
  cms_qe/settings/base/cache.py,sha256=9p6C5lOz1pG-6k15PyvxlShUjBYIbU0ewpA8AX_YFus,297
51
53
  cms_qe/settings/base/cms.py,sha256=8icCNxcEp_KRDyP8-LXB21UurJL4wNysY39whAyt3I4,1855
@@ -78,6 +80,7 @@ cms_qe/templatetags/cms_qe_filters.py,sha256=ciYCXLuMVde-f_-B_7a5mHfAJPWPMxYEUMg
78
80
  cms_qe/templatetags/kwacros.py,sha256=r2oHLltu8BgKKlrpgzXgvLjbIqwcrH13Lww3zTF-nr8,6275
79
81
  cms_qe/views/__init__.py,sha256=3b5FCZ5MaqgiWglC7c5mfvP3WYLWTtNp3YpVb9BgYi8,106
80
82
  cms_qe/views/errors.py,sha256=zUbCoyXy_MPsQv3UV1mgq-q2bwqPw9G4KgKU2-oue4w,3169
83
+ cms_qe/views/maintenance.py,sha256=rBfovOGETlfGNF7jlv-vuTEfjZIb5x9dYCSIyFTgHgQ,1312
81
84
  cms_qe/views/monitoring.py,sha256=1r2s_jm6B6P0gEmiqjH9m3loUW3BmJivvpg6qcUOxVM,1909
82
85
  cms_qe/views/search_result.py,sha256=H1eMOmtONnWqBDsBvz3MartyHhh42vyi0jPoxWb0X8c,296
83
86
  cms_qe/views/security.py,sha256=SNdJe4NSm1vuOGchVF3VqlmFDopt9xYoO-b4Y0UxkFU,1108
@@ -86,6 +89,7 @@ cms_qe/whoosh/backend.py,sha256=YOVJGz3htWdcSUbYtX2a9VqywAU3EJP_EvxXHCY0QVA,5418
86
89
  cms_qe_analytical/LICENSE.txt,sha256=ptQIrnsiWFFf2LZ60DTAO6XA7CQYFuwhX1m4kzhv5_8,1072
87
90
  cms_qe_analytical/__init__.py,sha256=G2QJmxgo4HbUvG4GObM7PcCodfo8SM2auAVQoa36mBY,4189
88
91
  cms_qe_analytical/utils.py,sha256=oQRPUyGIWW4lfaVNBPuwBwvnLg-D2JWaRHgNQrwvjcU,5149
92
+ cms_qe_analytical/templates/cms_qe_analytical/google_tag_manager.html,sha256=MJJJWSWfKAx4Z_7NFvhILlZWs0sO4zWaV5wZmEDXmd4,473
89
93
  cms_qe_analytical/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
94
  cms_qe_analytical/templatetags/google_analytics.py,sha256=cN87jJzwVcjhqSS2JKTmPt9WntuBLnzxEcwgjrQMJSg,7370
91
95
  cms_qe_analytical/templatetags/piwik.py,sha256=EHOaojmewFpMqHzm_QCtCXGc_HGoiCJ_G-LPc8IfjM4,4131
@@ -3936,6 +3940,8 @@ cms_qe_video/templates/cms_qe/video/video_source_file.html,sha256=QJF5fs88s9Fznp
3936
3940
  cms_qe_video/templates/cms_qe/video/video_widget.html,sha256=Yumciq6bGlAYI1lYx5j9V6IF8QYrncNYygPTkXEz6Wk,925
3937
3941
  cms_qe_video/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3938
3942
  cms_qe_video/templatetags/cms_qe_video.py,sha256=NR_mGv91J0rEreZrQjCzaaXSrZsKvrSas12wMJ-Dg24,1168
3943
+ django_mail_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3944
+ django_mail_backends/filebased.py,sha256=tsvMFFlm7-U5krIly53DArYZmrDrNZwQowKHScE8qQ4,612
3939
3945
  example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3940
3946
  example/urls.py,sha256=H-IJsRGFVZGw6FD9gvK-0B0dLeSOsduziHvDvcHCQZ0,399
3941
3947
  example/wsgi.py,sha256=lCKhvtFZlorSIA8qYEqc3pZ1Oflrz_Tc696MWJ62ue4,396
@@ -3956,8 +3962,8 @@ test_selenium/pages/cms/__init__.py,sha256=_qe4YZYaQbrXp7Szmmeo4TUSkXlE5Rozu8E3t
3956
3962
  test_selenium/pages/cms/login.py,sha256=UPzJQcYff8NUAT4nvmfQoJQxzOJyPrJ_cKtH35NVfNg,521
3957
3963
  test_selenium/pages/cms/page.py,sha256=YQnpZkopfVnhoyQKpRDGqjNeV6xUl-pEHjEcZ9HRiPk,489
3958
3964
  test_selenium/pages/cms/wizard.py,sha256=yatbXH-rf1ap4O1hY0I13WikM3zkm_NrAiSK6bqENIU,545
3959
- django_cms_qe-3.1.0.dist-info/LICENSE,sha256=5wLaeUil0gfU9p8C4zn2Yu_PvZBNieUoYl0z9FcFWdA,1521
3960
- django_cms_qe-3.1.0.dist-info/METADATA,sha256=6kP2n_UW8LwIUQSYP4edBzVbs-5_SDdLPKBNDAYUS1Y,4631
3961
- django_cms_qe-3.1.0.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
3962
- django_cms_qe-3.1.0.dist-info/top_level.txt,sha256=Gx3iR7ga_mwVi-a0WHznNn16KhciCv31fUrdh0sr8IM,157
3963
- django_cms_qe-3.1.0.dist-info/RECORD,,
3965
+ django_cms_qe-3.2.0.dist-info/LICENSE,sha256=5wLaeUil0gfU9p8C4zn2Yu_PvZBNieUoYl0z9FcFWdA,1521
3966
+ django_cms_qe-3.2.0.dist-info/METADATA,sha256=6MBxVvDvwZR8z0sS-gYogsECKcYxogAu9YMsCNReG1o,4677
3967
+ django_cms_qe-3.2.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
3968
+ django_cms_qe-3.2.0.dist-info/top_level.txt,sha256=sns93tUvzAUoyPpolDHe0Orka1l44A4BJnYFko7EIKU,178
3969
+ django_cms_qe-3.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.3)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -8,5 +8,6 @@ cms_qe_newsletter
8
8
  cms_qe_table
9
9
  cms_qe_test
10
10
  cms_qe_video
11
+ django_mail_backends
11
12
  example
12
13
  test_selenium
File without changes
@@ -0,0 +1,17 @@
1
+ # EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
2
+ # EMAIL_BACKEND = 'django_mail_backends.filebased.EmailBackend'
3
+ import datetime
4
+ import os
5
+
6
+ from django.core.mail.backends.filebased import EmailBackend as DjangoEmailBackend
7
+
8
+
9
+ class EmailBackend(DjangoEmailBackend):
10
+
11
+ def _get_filename(self):
12
+ """Return a unique file name."""
13
+ if self._fname is None:
14
+ timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
15
+ fname = "%s-%s.eml" % (timestamp, abs(id(self)))
16
+ self._fname = os.path.join(self.file_path, fname)
17
+ return self._fname