djangocms-alias 2.0.0__tar.gz
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.
- djangocms-alias-2.0.0/LICENSE.txt +24 -0
- djangocms-alias-2.0.0/MANIFEST.in +6 -0
- djangocms-alias-2.0.0/PKG-INFO +116 -0
- djangocms-alias-2.0.0/README.rst +80 -0
- djangocms-alias-2.0.0/djangocms_alias/__init__.py +1 -0
- djangocms-alias-2.0.0/djangocms_alias/admin.py +175 -0
- djangocms-alias-2.0.0/djangocms_alias/apps.py +7 -0
- djangocms-alias-2.0.0/djangocms_alias/cms_config.py +57 -0
- djangocms-alias-2.0.0/djangocms_alias/cms_menus.py +20 -0
- djangocms-alias-2.0.0/djangocms_alias/cms_plugins.py +165 -0
- djangocms-alias-2.0.0/djangocms_alias/cms_toolbars.py +290 -0
- djangocms-alias-2.0.0/djangocms_alias/cms_wizards.py +42 -0
- djangocms-alias-2.0.0/djangocms_alias/compat.py +6 -0
- djangocms-alias-2.0.0/djangocms_alias/constants.py +22 -0
- djangocms-alias-2.0.0/djangocms_alias/filters.py +87 -0
- djangocms-alias-2.0.0/djangocms_alias/forms.py +343 -0
- djangocms-alias-2.0.0/djangocms_alias/internal_search.py +92 -0
- djangocms-alias-2.0.0/djangocms_alias/locale/de/LC_MESSAGES/django.mo +0 -0
- djangocms-alias-2.0.0/djangocms_alias/locale/de/LC_MESSAGES/django.po +346 -0
- djangocms-alias-2.0.0/djangocms_alias/locale/en/LC_MESSAGES/django.mo +0 -0
- djangocms-alias-2.0.0/djangocms_alias/locale/en/LC_MESSAGES/django.po +330 -0
- djangocms-alias-2.0.0/djangocms_alias/locale/nl/LC_MESSAGES/django.mo +0 -0
- djangocms-alias-2.0.0/djangocms_alias/locale/nl/LC_MESSAGES/django.po +343 -0
- djangocms-alias-2.0.0/djangocms_alias/locale/sq/LC_MESSAGES/django.mo +0 -0
- djangocms-alias-2.0.0/djangocms_alias/locale/sq/LC_MESSAGES/django.po +341 -0
- djangocms-alias-2.0.0/djangocms_alias/migrations/0001_initial.py +95 -0
- djangocms-alias-2.0.0/djangocms_alias/migrations/0002_auto_20200723_1424.py +40 -0
- djangocms-alias-2.0.0/djangocms_alias/migrations/0003_auto_20230725_1547.py +28 -0
- djangocms-alias-2.0.0/djangocms_alias/migrations/0004_alter_aliascontent_language.py +19 -0
- djangocms-alias-2.0.0/djangocms_alias/migrations/__init__.py +0 -0
- djangocms-alias-2.0.0/djangocms_alias/models.py +419 -0
- djangocms-alias-2.0.0/djangocms_alias/rendering.py +7 -0
- djangocms-alias-2.0.0/djangocms_alias/static/djangocms_alias/js/alias_plugin.js +12 -0
- djangocms-alias-2.0.0/djangocms_alias/static/djangocms_alias/js/create.js +91 -0
- djangocms-alias-2.0.0/djangocms_alias/static/djangocms_alias/js/dist/bundle.alias.create.min.js +1 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/admin/djangocms_alias/alias/change_form.html +10 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/admin/djangocms_alias/alias/delete_confirmation.html +46 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/admin/djangocms_alias/aliascontent/change_form.html +16 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/add_alias.html +31 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/alias_content.html +1 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/alias_content_preview.html +15 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/alias_recursive.html +0 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/alias_replace.html +41 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/alias_usage.html +55 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/base.html +14 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/create_alias.html +34 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/default/alias.html +1 -0
- djangocms-alias-2.0.0/djangocms_alias/templates/djangocms_alias/detach_alias.html +20 -0
- djangocms-alias-2.0.0/djangocms_alias/templatetags/__init__.py +0 -0
- djangocms-alias-2.0.0/djangocms_alias/templatetags/djangocms_alias_tags.py +185 -0
- djangocms-alias-2.0.0/djangocms_alias/test_utils/__init__.py +0 -0
- djangocms-alias-2.0.0/djangocms_alias/test_utils/text/__init__.py +0 -0
- djangocms-alias-2.0.0/djangocms_alias/test_utils/text/cms_plugins.py +12 -0
- djangocms-alias-2.0.0/djangocms_alias/test_utils/text/models.py +10 -0
- djangocms-alias-2.0.0/djangocms_alias/urls.py +40 -0
- djangocms-alias-2.0.0/djangocms_alias/utils.py +30 -0
- djangocms-alias-2.0.0/djangocms_alias/views.py +316 -0
- djangocms-alias-2.0.0/djangocms_alias.egg-info/PKG-INFO +116 -0
- djangocms-alias-2.0.0/djangocms_alias.egg-info/SOURCES.txt +76 -0
- djangocms-alias-2.0.0/djangocms_alias.egg-info/dependency_links.txt +1 -0
- djangocms-alias-2.0.0/djangocms_alias.egg-info/requires.txt +3 -0
- djangocms-alias-2.0.0/djangocms_alias.egg-info/top_level.txt +2 -0
- djangocms-alias-2.0.0/setup.cfg +64 -0
- djangocms-alias-2.0.0/setup.py +53 -0
- djangocms-alias-2.0.0/tests/__init__.py +0 -0
- djangocms-alias-2.0.0/tests/base.py +218 -0
- djangocms-alias-2.0.0/tests/test_admin.py +582 -0
- djangocms-alias-2.0.0/tests/test_admin_filters.py +302 -0
- djangocms-alias-2.0.0/tests/test_cms_config.py +29 -0
- djangocms-alias-2.0.0/tests/test_cms_plugins.py +362 -0
- djangocms-alias-2.0.0/tests/test_menu.py +26 -0
- djangocms-alias-2.0.0/tests/test_models.py +449 -0
- djangocms-alias-2.0.0/tests/test_permissions.py +111 -0
- djangocms-alias-2.0.0/tests/test_templatetags.py +365 -0
- djangocms-alias-2.0.0/tests/test_toolbar.py +457 -0
- djangocms-alias-2.0.0/tests/test_views.py +1521 -0
- djangocms-alias-2.0.0/tests/test_wizards.py +141 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Copyright (c) 2018, Divio AG
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
* Redistributions of source code must retain the above copyright
|
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
|
10
|
+
documentation and/or other materials provided with the distribution.
|
|
11
|
+
* Neither the name of Divio AG nor the
|
|
12
|
+
names of its contributors may be used to endorse or promote products
|
|
13
|
+
derived from this software without specific prior written permission.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL DIVIO AG BE LIABLE FOR ANY
|
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: djangocms-alias
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Home-page: https://github.com/django-cms/djangocms-alias
|
|
5
|
+
Author: Divio AG
|
|
6
|
+
Author-email: info@divio.ch
|
|
7
|
+
Maintainer: Django CMS Association and contributors
|
|
8
|
+
Maintainer-email: info@django-cms.org
|
|
9
|
+
License: BSD
|
|
10
|
+
Platform: OS Independent
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
18
|
+
Classifier: Topic :: Software Development
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Framework :: Django
|
|
25
|
+
Classifier: Framework :: Django :: 3.2
|
|
26
|
+
Classifier: Framework :: Django :: 4.0
|
|
27
|
+
Classifier: Framework :: Django :: 4.1
|
|
28
|
+
Classifier: Framework :: Django :: 4.2
|
|
29
|
+
Classifier: Framework :: Django :: 5.0
|
|
30
|
+
Classifier: Framework :: Django CMS :: 4.1
|
|
31
|
+
Description-Content-Type: text/x-rst
|
|
32
|
+
License-File: LICENSE.txt
|
|
33
|
+
Requires-Dist: Django>=3.2
|
|
34
|
+
Requires-Dist: django-parler>=1.4
|
|
35
|
+
Requires-Dist: django-cms>=4.0rc3
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
****************
|
|
40
|
+
django CMS Alias
|
|
41
|
+
****************
|
|
42
|
+
|
|
43
|
+
|coverage| |python| |django| |djangocms4|
|
|
44
|
+
|
|
45
|
+
django CMS Alias replicates and extends the alias function of django CMS version 3 for django CMS version 4.
|
|
46
|
+
|
|
47
|
+
An alias is a collection of plugins that is managed centrally. A reference can be added to any placeholder using the Alias plugin. Since the Alias plugin creates a reference any changes to the alias are immediately reflected at all places it is used.
|
|
48
|
+
|
|
49
|
+
django CMS Alias supports versioning aliases by django CMS Versioning.
|
|
50
|
+
|
|
51
|
+
.. warning::
|
|
52
|
+
|
|
53
|
+
This is the development branch for django CMS version 4.1 support.
|
|
54
|
+
|
|
55
|
+
For django CMS V4.0 support, see `support/django-cms-4.0.x branch <https://github.com/django-cms/djangocms-alias/tree/support/django-cms-4.0.x>`_
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
============
|
|
59
|
+
Installation
|
|
60
|
+
============
|
|
61
|
+
|
|
62
|
+
Requirements
|
|
63
|
+
============
|
|
64
|
+
|
|
65
|
+
django CMS Alias requires that you have a django CMS 4 (or higher) project already running and set up.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
To install
|
|
69
|
+
==========
|
|
70
|
+
|
|
71
|
+
Run::
|
|
72
|
+
|
|
73
|
+
pip install git+https://github.com/django-cms/djangocms-alias@master#egg=djangocms-alias
|
|
74
|
+
|
|
75
|
+
Add ``djangocms_alias`` and ``parler`` to your project's ``INSTALLED_APPS``.
|
|
76
|
+
|
|
77
|
+
Run::
|
|
78
|
+
|
|
79
|
+
python manage.py migrate djangocms_alias
|
|
80
|
+
|
|
81
|
+
to perform the application's database migrations.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
=====
|
|
85
|
+
Usage
|
|
86
|
+
=====
|
|
87
|
+
|
|
88
|
+
Static aliases
|
|
89
|
+
==============
|
|
90
|
+
|
|
91
|
+
Static aliases appear in templates and replace static placeholders which were part of django CMS up to version 3.x.
|
|
92
|
+
|
|
93
|
+
Example::
|
|
94
|
+
|
|
95
|
+
{% load djangocms_alias_tags %}
|
|
96
|
+
...
|
|
97
|
+
<footer>
|
|
98
|
+
{% static_alias 'footer' %}
|
|
99
|
+
</footer>
|
|
100
|
+
|
|
101
|
+
Alias plugin
|
|
102
|
+
============
|
|
103
|
+
|
|
104
|
+
Alternatively, aliases can be used with the Alias plugin. It allows to select which alias content is shown at the exact position the alias plugin is placed.
|
|
105
|
+
|
|
106
|
+
.. |coverage| image:: https://codecov.io/gh/django-cms/djangocms-alias/branch/master/graph/badge.svg
|
|
107
|
+
:target: https://codecov.io/gh/django-cms/djangocms-alias
|
|
108
|
+
|
|
109
|
+
.. |python| image:: https://img.shields.io/badge/python-3.7+-blue.svg
|
|
110
|
+
:target: https://pypi.org/project/djangocms-alias/
|
|
111
|
+
|
|
112
|
+
.. |django| image:: https://img.shields.io/badge/django-3.2--4.1-blue.svg
|
|
113
|
+
:target: https://www.djangoproject.com/
|
|
114
|
+
|
|
115
|
+
.. |djangocms4| image:: https://img.shields.io/badge/django%20CMS-4-blue.svg
|
|
116
|
+
:target: https://www.django-cms.org/
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
****************
|
|
4
|
+
django CMS Alias
|
|
5
|
+
****************
|
|
6
|
+
|
|
7
|
+
|coverage| |python| |django| |djangocms4|
|
|
8
|
+
|
|
9
|
+
django CMS Alias replicates and extends the alias function of django CMS version 3 for django CMS version 4.
|
|
10
|
+
|
|
11
|
+
An alias is a collection of plugins that is managed centrally. A reference can be added to any placeholder using the Alias plugin. Since the Alias plugin creates a reference any changes to the alias are immediately reflected at all places it is used.
|
|
12
|
+
|
|
13
|
+
django CMS Alias supports versioning aliases by django CMS Versioning.
|
|
14
|
+
|
|
15
|
+
.. warning::
|
|
16
|
+
|
|
17
|
+
This is the development branch for django CMS version 4.1 support.
|
|
18
|
+
|
|
19
|
+
For django CMS V4.0 support, see `support/django-cms-4.0.x branch <https://github.com/django-cms/djangocms-alias/tree/support/django-cms-4.0.x>`_
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
============
|
|
23
|
+
Installation
|
|
24
|
+
============
|
|
25
|
+
|
|
26
|
+
Requirements
|
|
27
|
+
============
|
|
28
|
+
|
|
29
|
+
django CMS Alias requires that you have a django CMS 4 (or higher) project already running and set up.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
To install
|
|
33
|
+
==========
|
|
34
|
+
|
|
35
|
+
Run::
|
|
36
|
+
|
|
37
|
+
pip install git+https://github.com/django-cms/djangocms-alias@master#egg=djangocms-alias
|
|
38
|
+
|
|
39
|
+
Add ``djangocms_alias`` and ``parler`` to your project's ``INSTALLED_APPS``.
|
|
40
|
+
|
|
41
|
+
Run::
|
|
42
|
+
|
|
43
|
+
python manage.py migrate djangocms_alias
|
|
44
|
+
|
|
45
|
+
to perform the application's database migrations.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
=====
|
|
49
|
+
Usage
|
|
50
|
+
=====
|
|
51
|
+
|
|
52
|
+
Static aliases
|
|
53
|
+
==============
|
|
54
|
+
|
|
55
|
+
Static aliases appear in templates and replace static placeholders which were part of django CMS up to version 3.x.
|
|
56
|
+
|
|
57
|
+
Example::
|
|
58
|
+
|
|
59
|
+
{% load djangocms_alias_tags %}
|
|
60
|
+
...
|
|
61
|
+
<footer>
|
|
62
|
+
{% static_alias 'footer' %}
|
|
63
|
+
</footer>
|
|
64
|
+
|
|
65
|
+
Alias plugin
|
|
66
|
+
============
|
|
67
|
+
|
|
68
|
+
Alternatively, aliases can be used with the Alias plugin. It allows to select which alias content is shown at the exact position the alias plugin is placed.
|
|
69
|
+
|
|
70
|
+
.. |coverage| image:: https://codecov.io/gh/django-cms/djangocms-alias/branch/master/graph/badge.svg
|
|
71
|
+
:target: https://codecov.io/gh/django-cms/djangocms-alias
|
|
72
|
+
|
|
73
|
+
.. |python| image:: https://img.shields.io/badge/python-3.7+-blue.svg
|
|
74
|
+
:target: https://pypi.org/project/djangocms-alias/
|
|
75
|
+
|
|
76
|
+
.. |django| image:: https://img.shields.io/badge/django-3.2--4.1-blue.svg
|
|
77
|
+
:target: https://www.djangoproject.com/
|
|
78
|
+
|
|
79
|
+
.. |djangocms4| image:: https://img.shields.io/badge/django%20CMS-4-blue.svg
|
|
80
|
+
:target: https://www.django-cms.org/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.0.0'
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.contrib import admin
|
|
3
|
+
from django.http import (
|
|
4
|
+
Http404,
|
|
5
|
+
HttpRequest,
|
|
6
|
+
HttpResponse,
|
|
7
|
+
HttpResponseRedirect,
|
|
8
|
+
)
|
|
9
|
+
from django.utils.safestring import mark_safe
|
|
10
|
+
from django.utils.translation import gettext_lazy as _
|
|
11
|
+
|
|
12
|
+
from cms.admin.utils import GrouperModelAdmin
|
|
13
|
+
from cms.utils.permissions import get_model_permission_codename
|
|
14
|
+
from cms.utils.urlutils import admin_reverse
|
|
15
|
+
|
|
16
|
+
from parler.admin import TranslatableAdmin
|
|
17
|
+
|
|
18
|
+
from .cms_config import AliasCMSConfig
|
|
19
|
+
from .constants import (
|
|
20
|
+
CHANGE_ALIAS_URL_NAME,
|
|
21
|
+
DELETE_ALIAS_URL_NAME,
|
|
22
|
+
LIST_ALIAS_URL_NAME,
|
|
23
|
+
USAGE_ALIAS_URL_NAME,
|
|
24
|
+
)
|
|
25
|
+
from .filters import CategoryFilter, SiteFilter
|
|
26
|
+
from .forms import AliasGrouperAdminForm
|
|
27
|
+
from .models import Alias, AliasContent, Category
|
|
28
|
+
from .urls import urlpatterns
|
|
29
|
+
from .utils import (
|
|
30
|
+
emit_content_change,
|
|
31
|
+
emit_content_delete,
|
|
32
|
+
is_versioning_enabled,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
'AliasAdmin',
|
|
38
|
+
'CategoryAdmin',
|
|
39
|
+
'AliasContentAdmin',
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
alias_admin_classes = [GrouperModelAdmin]
|
|
43
|
+
alias_admin_list_display = ['content__name', 'category', 'admin_list_actions']
|
|
44
|
+
djangocms_versioning_enabled = AliasCMSConfig.djangocms_versioning_enabled
|
|
45
|
+
|
|
46
|
+
if djangocms_versioning_enabled:
|
|
47
|
+
from djangocms_versioning.admin import (
|
|
48
|
+
ExtendedGrouperVersionAdminMixin,
|
|
49
|
+
StateIndicatorMixin,
|
|
50
|
+
)
|
|
51
|
+
from djangocms_versioning.models import Version
|
|
52
|
+
|
|
53
|
+
alias_admin_classes.insert(0, ExtendedGrouperVersionAdminMixin)
|
|
54
|
+
alias_admin_classes.insert(0, StateIndicatorMixin)
|
|
55
|
+
alias_admin_list_display.insert(-1, "get_author")
|
|
56
|
+
alias_admin_list_display.insert(-1, "get_modified_date")
|
|
57
|
+
alias_admin_list_display.insert(-1, "state_indicator")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@admin.register(Category)
|
|
61
|
+
class CategoryAdmin(TranslatableAdmin):
|
|
62
|
+
list_display = ['name']
|
|
63
|
+
|
|
64
|
+
def save_model(self, request, obj, form, change):
|
|
65
|
+
change = not obj._state.adding
|
|
66
|
+
super().save_model(request, obj, form, change)
|
|
67
|
+
if change:
|
|
68
|
+
# Don't emit delete content because there is on_delete=PROTECT for
|
|
69
|
+
# category FK on alias
|
|
70
|
+
emit_content_change(
|
|
71
|
+
AliasContent._base_manager.filter(alias__in=obj.aliases.all()),
|
|
72
|
+
sender=self.model,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@admin.register(Alias)
|
|
77
|
+
class AliasAdmin(*alias_admin_classes):
|
|
78
|
+
list_display = alias_admin_list_display
|
|
79
|
+
list_display_links = None
|
|
80
|
+
list_filter = (SiteFilter, CategoryFilter,)
|
|
81
|
+
fields = ('content__name', 'category', 'site', 'content__language')
|
|
82
|
+
readonly_fields = ('static_code', )
|
|
83
|
+
form = AliasGrouperAdminForm
|
|
84
|
+
extra_grouping_fields = ("language",)
|
|
85
|
+
EMPTY_CONTENT_VALUE = mark_safe(_("<i>Missing language</i>"))
|
|
86
|
+
|
|
87
|
+
def get_urls(self) -> list:
|
|
88
|
+
return urlpatterns + super().get_urls()
|
|
89
|
+
|
|
90
|
+
def get_actions_list(self) -> list:
|
|
91
|
+
"""Add alias usage list actions"""
|
|
92
|
+
return super().get_actions_list() + [self._get_alias_usage_link]
|
|
93
|
+
|
|
94
|
+
def can_change_content(self, request: HttpRequest, content_obj: AliasContent) -> bool:
|
|
95
|
+
"""Returns True if user can change content_obj"""
|
|
96
|
+
if content_obj and is_versioning_enabled():
|
|
97
|
+
version = Version.objects.get_for_content(content_obj)
|
|
98
|
+
return version.check_modify.as_bool(request.user)
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
def has_delete_permission(self, request: HttpRequest, obj: Alias = None) -> bool:
|
|
102
|
+
# Alias can be deleted by users who can add aliases,
|
|
103
|
+
# if that alias is not referenced anywhere.
|
|
104
|
+
if obj:
|
|
105
|
+
if not obj.is_in_use:
|
|
106
|
+
return request.user.has_perm(
|
|
107
|
+
get_model_permission_codename(self.model, 'add'),
|
|
108
|
+
)
|
|
109
|
+
return request.user.is_superuser
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
def save_model(self, request: HttpRequest, obj: Alias, form: forms.Form, change: bool) -> None:
|
|
113
|
+
super().save_model(request, obj, form, change)
|
|
114
|
+
|
|
115
|
+
# Only emit content changes if Versioning is not installed because
|
|
116
|
+
# Versioning emits its own signals for changes
|
|
117
|
+
if not is_versioning_enabled():
|
|
118
|
+
emit_content_change(
|
|
119
|
+
AliasContent._base_manager.filter(alias=obj),
|
|
120
|
+
sender=self.model,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def get_deleted_objects(self, objs, request: HttpRequest) -> tuple:
|
|
124
|
+
deleted_objects, model_count, perms_needed, protected = super().get_deleted_objects(objs, request)
|
|
125
|
+
# This is bad and I should feel bad.
|
|
126
|
+
if 'placeholder' in perms_needed:
|
|
127
|
+
perms_needed.remove('placeholder')
|
|
128
|
+
return deleted_objects, model_count, perms_needed, protected
|
|
129
|
+
|
|
130
|
+
def delete_model(self, request: HttpRequest, obj: Alias):
|
|
131
|
+
super().delete_model(request, obj)
|
|
132
|
+
|
|
133
|
+
# Only emit content changes if Versioning is not installed because
|
|
134
|
+
# Versioning emits it' own signals for changes
|
|
135
|
+
if not is_versioning_enabled():
|
|
136
|
+
emit_content_delete(
|
|
137
|
+
AliasContent._base_manager.filter(alias=obj),
|
|
138
|
+
sender=self.model,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def _get_alias_usage_link(self, obj: Alias, request: HttpRequest, disabled: bool = False) -> str:
|
|
142
|
+
url = admin_reverse(USAGE_ALIAS_URL_NAME, args=[obj.pk])
|
|
143
|
+
return self.admin_action_button(url, "info", _("View usage"), disabled=disabled)
|
|
144
|
+
|
|
145
|
+
def _get_alias_delete_link(self, obj: Alias, request: HttpRequest) -> str:
|
|
146
|
+
url = admin_reverse(DELETE_ALIAS_URL_NAME, args=[obj.pk])
|
|
147
|
+
return self.admin_action_button(url, "bin", _("Delete Alias"),
|
|
148
|
+
disabled=not self.has_delete_permission(request, obj))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@admin.register(AliasContent)
|
|
152
|
+
class AliasContentAdmin(admin.ModelAdmin):
|
|
153
|
+
# Disable dropdown actions
|
|
154
|
+
actions = None
|
|
155
|
+
change_form_template = "admin/djangocms_alias/aliascontent/change_form.html"
|
|
156
|
+
|
|
157
|
+
def changelist_view(self, request: HttpRequest, extra_context: dict = None) -> HttpResponse:
|
|
158
|
+
"""Needed for the Alias Content Admin breadcrumbs"""
|
|
159
|
+
return HttpResponseRedirect(admin_reverse(
|
|
160
|
+
LIST_ALIAS_URL_NAME,
|
|
161
|
+
))
|
|
162
|
+
|
|
163
|
+
def change_view(self, request: HttpRequest, object_id: int, form_url: str = '', extra_context: dict = None) -> HttpResponse:
|
|
164
|
+
"""Needed for the Alias Content Admin breadcrumbs"""
|
|
165
|
+
obj = self.model.admin_manager.filter(pk=object_id).first()
|
|
166
|
+
if not obj:
|
|
167
|
+
raise Http404()
|
|
168
|
+
return HttpResponseRedirect(admin_reverse(
|
|
169
|
+
CHANGE_ALIAS_URL_NAME, args=(obj.alias_id,)
|
|
170
|
+
) + f"?language={obj.language}")
|
|
171
|
+
|
|
172
|
+
def has_module_permission(self, request: HttpRequest) -> bool:
|
|
173
|
+
"""Hides admin class in admin site overview"""
|
|
174
|
+
|
|
175
|
+
return False
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from django.apps import apps
|
|
2
|
+
from django.conf import settings
|
|
3
|
+
|
|
4
|
+
from cms.app_base import CMSAppConfig
|
|
5
|
+
|
|
6
|
+
from .models import AliasContent, AliasPlugin, copy_alias_content
|
|
7
|
+
from .rendering import render_alias_content
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
apps.get_app_config('djangocms_internalsearch')
|
|
12
|
+
from .internal_search import AliasContentConfig
|
|
13
|
+
except (ImportError, LookupError):
|
|
14
|
+
AliasContentConfig = None
|
|
15
|
+
|
|
16
|
+
djangocms_versioning_installed = apps.is_installed("djangocms_versioning")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AliasCMSConfig(CMSAppConfig):
|
|
20
|
+
cms_enabled = True
|
|
21
|
+
cms_toolbar_enabled_models = [(AliasContent, render_alias_content)]
|
|
22
|
+
moderated_models = [AliasContent]
|
|
23
|
+
|
|
24
|
+
djangocms_moderation_enabled = getattr(
|
|
25
|
+
settings, 'MODERATING_ALIAS_MODELS_ENABLED', True)
|
|
26
|
+
djangocms_versioning_enabled = getattr(
|
|
27
|
+
settings, 'VERSIONING_ALIAS_MODELS_ENABLED', True) and djangocms_versioning_installed
|
|
28
|
+
|
|
29
|
+
if djangocms_versioning_enabled:
|
|
30
|
+
|
|
31
|
+
from cms.utils.i18n import get_language_tuple
|
|
32
|
+
|
|
33
|
+
from djangocms_versioning.datastructures import VersionableItem
|
|
34
|
+
|
|
35
|
+
versioning = [
|
|
36
|
+
VersionableItem(
|
|
37
|
+
content_model=AliasContent,
|
|
38
|
+
grouper_field_name='alias',
|
|
39
|
+
extra_grouping_fields=["language"],
|
|
40
|
+
version_list_filter_lookups={"language": get_language_tuple},
|
|
41
|
+
copy_function=copy_alias_content,
|
|
42
|
+
grouper_selector_option_label=lambda obj, lang: obj.get_name(lang),
|
|
43
|
+
),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
djangocms_references_enabled = getattr(
|
|
47
|
+
settings, 'REFERENCES_ALIAS_MODELS_ENABLED', True)
|
|
48
|
+
reference_fields = [
|
|
49
|
+
(AliasPlugin, 'alias'),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# Internalsearch configuration
|
|
53
|
+
if AliasContentConfig:
|
|
54
|
+
djangocms_internalsearch_enabled = True
|
|
55
|
+
internalsearch_config_list = [
|
|
56
|
+
AliasContentConfig,
|
|
57
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from menus.base import Modifier
|
|
2
|
+
from menus.menu_pool import menu_pool
|
|
3
|
+
|
|
4
|
+
from .constants import PLUGIN_URL_NAME_PREFIX
|
|
5
|
+
from .models import AliasContent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AliasDisableMenu(Modifier):
|
|
9
|
+
"""Disable menu rendering on alias pages"""
|
|
10
|
+
|
|
11
|
+
def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
|
|
12
|
+
if (
|
|
13
|
+
request.toolbar.app_name == PLUGIN_URL_NAME_PREFIX
|
|
14
|
+
or isinstance(request.toolbar.obj, AliasContent)
|
|
15
|
+
):
|
|
16
|
+
return []
|
|
17
|
+
return nodes
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
menu_pool.register_modifier(AliasDisableMenu)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from copy import copy
|
|
2
|
+
|
|
3
|
+
from django.utils.translation import (
|
|
4
|
+
get_language_from_request,
|
|
5
|
+
gettext_lazy as _,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from cms.plugin_base import CMSPluginBase, PluginMenuItem
|
|
9
|
+
from cms.plugin_pool import plugin_pool
|
|
10
|
+
from cms.utils.permissions import (
|
|
11
|
+
get_model_permission_codename,
|
|
12
|
+
has_plugin_permission,
|
|
13
|
+
)
|
|
14
|
+
from cms.utils.plugins import copy_plugins_to_placeholder
|
|
15
|
+
from cms.utils.urlutils import add_url_parameters, admin_reverse
|
|
16
|
+
|
|
17
|
+
from .constants import CREATE_ALIAS_URL_NAME, DETACH_ALIAS_PLUGIN_URL_NAME
|
|
18
|
+
from .forms import AliasPluginForm
|
|
19
|
+
from .models import Alias as AliasModel, AliasContent, AliasPlugin
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
'Alias',
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@plugin_pool.register_plugin
|
|
28
|
+
class Alias(CMSPluginBase):
|
|
29
|
+
name = _('Alias')
|
|
30
|
+
model = AliasPlugin
|
|
31
|
+
form = AliasPluginForm
|
|
32
|
+
|
|
33
|
+
def get_render_template(self, context, instance, placeholder):
|
|
34
|
+
if (
|
|
35
|
+
isinstance(instance.placeholder.source, AliasContent)
|
|
36
|
+
and instance.is_recursive()
|
|
37
|
+
):
|
|
38
|
+
return 'djangocms_alias/alias_recursive.html'
|
|
39
|
+
return f'djangocms_alias/{instance.template}/alias.html'
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def get_extra_plugin_menu_items(cls, request, plugin):
|
|
43
|
+
if plugin.plugin_type == cls.__name__:
|
|
44
|
+
edit_endpoint = plugin.alias.get_absolute_url()
|
|
45
|
+
detach_endpoint = admin_reverse(
|
|
46
|
+
DETACH_ALIAS_PLUGIN_URL_NAME,
|
|
47
|
+
args=[plugin.pk],
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
plugin_menu_items = [
|
|
51
|
+
PluginMenuItem(
|
|
52
|
+
_('Edit Alias'),
|
|
53
|
+
edit_endpoint,
|
|
54
|
+
action='sideframe',
|
|
55
|
+
attributes={'cms-icon': 'alias'},
|
|
56
|
+
),
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
if cls.can_detach(
|
|
60
|
+
request.user,
|
|
61
|
+
plugin.placeholder,
|
|
62
|
+
plugin.alias.get_plugins(),
|
|
63
|
+
):
|
|
64
|
+
plugin_menu_items.append(
|
|
65
|
+
PluginMenuItem(
|
|
66
|
+
_('Detach Alias'),
|
|
67
|
+
detach_endpoint,
|
|
68
|
+
action='modal',
|
|
69
|
+
attributes={'cms-icon': 'alias'},
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
return plugin_menu_items
|
|
73
|
+
|
|
74
|
+
data = {
|
|
75
|
+
'plugin': plugin.pk,
|
|
76
|
+
'language': get_language_from_request(request, check_path=True),
|
|
77
|
+
}
|
|
78
|
+
endpoint = add_url_parameters(admin_reverse(CREATE_ALIAS_URL_NAME), **data)
|
|
79
|
+
return [
|
|
80
|
+
PluginMenuItem(
|
|
81
|
+
_('Create Alias'),
|
|
82
|
+
endpoint,
|
|
83
|
+
action='modal',
|
|
84
|
+
attributes={'cms-icon': 'alias'},
|
|
85
|
+
),
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def get_extra_placeholder_menu_items(cls, request, placeholder):
|
|
90
|
+
data = {
|
|
91
|
+
'placeholder': placeholder.pk,
|
|
92
|
+
'language': get_language_from_request(request, check_path=True),
|
|
93
|
+
}
|
|
94
|
+
endpoint = add_url_parameters(admin_reverse(CREATE_ALIAS_URL_NAME), **data)
|
|
95
|
+
|
|
96
|
+
menu_items = [
|
|
97
|
+
PluginMenuItem(
|
|
98
|
+
_('Create Alias'),
|
|
99
|
+
endpoint,
|
|
100
|
+
action='modal',
|
|
101
|
+
attributes={'cms-icon': 'alias'},
|
|
102
|
+
),
|
|
103
|
+
]
|
|
104
|
+
return menu_items
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def can_create_alias(cls, user, plugins=None, replace=False):
|
|
108
|
+
if not user.has_perm(
|
|
109
|
+
get_model_permission_codename(AliasModel, 'add'),
|
|
110
|
+
):
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
if not plugins:
|
|
114
|
+
return True
|
|
115
|
+
elif replace:
|
|
116
|
+
target_placeholder = plugins[0].placeholder
|
|
117
|
+
if (
|
|
118
|
+
not target_placeholder.check_source(user)
|
|
119
|
+
or not has_plugin_permission(user, Alias.__name__, 'add')
|
|
120
|
+
):
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
return all(
|
|
124
|
+
has_plugin_permission(
|
|
125
|
+
user,
|
|
126
|
+
plugin.plugin_type,
|
|
127
|
+
'add',
|
|
128
|
+
) for plugin in plugins
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def can_detach(cls, user, target_placeholder, plugins):
|
|
133
|
+
return all(
|
|
134
|
+
has_plugin_permission(
|
|
135
|
+
user,
|
|
136
|
+
plugin.plugin_type,
|
|
137
|
+
'add',
|
|
138
|
+
) for plugin in plugins
|
|
139
|
+
) and target_placeholder.check_source(user)
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def detach_alias_plugin(cls, plugin, language):
|
|
143
|
+
source_placeholder = plugin.alias.get_placeholder(language, show_draft_content=True) # We're in edit mode
|
|
144
|
+
target_placeholder = plugin.placeholder
|
|
145
|
+
|
|
146
|
+
# Deleting uses a copy of a plugin to preserve pk on existing
|
|
147
|
+
# ``plugin`` object. This is done due to
|
|
148
|
+
# plugin.get_plugin_toolbar_info requiring a PK in a passed
|
|
149
|
+
# instance.
|
|
150
|
+
target_placeholder.delete_plugin(copy(plugin))
|
|
151
|
+
target_placeholder._shift_plugin_positions(
|
|
152
|
+
language,
|
|
153
|
+
plugin.position,
|
|
154
|
+
offset=target_placeholder.get_last_plugin_position(language),
|
|
155
|
+
)
|
|
156
|
+
if source_placeholder:
|
|
157
|
+
source_plugins = source_placeholder.get_plugins_list()
|
|
158
|
+
copied_plugins = copy_plugins_to_placeholder(
|
|
159
|
+
source_plugins,
|
|
160
|
+
placeholder=target_placeholder,
|
|
161
|
+
language=language,
|
|
162
|
+
start_positions={language: plugin.position},
|
|
163
|
+
)
|
|
164
|
+
return copied_plugins
|
|
165
|
+
return []
|