djangocms-alias 2.0.0rc1__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.
Files changed (75) hide show
  1. djangocms-alias-2.0.0rc1/LICENSE.txt +24 -0
  2. djangocms-alias-2.0.0rc1/MANIFEST.in +6 -0
  3. djangocms-alias-2.0.0rc1/PKG-INFO +112 -0
  4. djangocms-alias-2.0.0rc1/README.rst +80 -0
  5. djangocms-alias-2.0.0rc1/djangocms_alias/__init__.py +1 -0
  6. djangocms-alias-2.0.0rc1/djangocms_alias/admin.py +175 -0
  7. djangocms-alias-2.0.0rc1/djangocms_alias/apps.py +7 -0
  8. djangocms-alias-2.0.0rc1/djangocms_alias/cms_config.py +57 -0
  9. djangocms-alias-2.0.0rc1/djangocms_alias/cms_menus.py +20 -0
  10. djangocms-alias-2.0.0rc1/djangocms_alias/cms_plugins.py +163 -0
  11. djangocms-alias-2.0.0rc1/djangocms_alias/cms_toolbars.py +290 -0
  12. djangocms-alias-2.0.0rc1/djangocms_alias/cms_wizards.py +42 -0
  13. djangocms-alias-2.0.0rc1/djangocms_alias/compat.py +6 -0
  14. djangocms-alias-2.0.0rc1/djangocms_alias/constants.py +22 -0
  15. djangocms-alias-2.0.0rc1/djangocms_alias/filters.py +87 -0
  16. djangocms-alias-2.0.0rc1/djangocms_alias/forms.py +343 -0
  17. djangocms-alias-2.0.0rc1/djangocms_alias/internal_search.py +92 -0
  18. djangocms-alias-2.0.0rc1/djangocms_alias/locale/de/LC_MESSAGES/django.mo +0 -0
  19. djangocms-alias-2.0.0rc1/djangocms_alias/locale/de/LC_MESSAGES/django.po +346 -0
  20. djangocms-alias-2.0.0rc1/djangocms_alias/locale/en/LC_MESSAGES/django.mo +0 -0
  21. djangocms-alias-2.0.0rc1/djangocms_alias/locale/en/LC_MESSAGES/django.po +330 -0
  22. djangocms-alias-2.0.0rc1/djangocms_alias/locale/nl/LC_MESSAGES/django.mo +0 -0
  23. djangocms-alias-2.0.0rc1/djangocms_alias/locale/nl/LC_MESSAGES/django.po +343 -0
  24. djangocms-alias-2.0.0rc1/djangocms_alias/locale/sq/LC_MESSAGES/django.mo +0 -0
  25. djangocms-alias-2.0.0rc1/djangocms_alias/locale/sq/LC_MESSAGES/django.po +341 -0
  26. djangocms-alias-2.0.0rc1/djangocms_alias/migrations/0001_initial.py +95 -0
  27. djangocms-alias-2.0.0rc1/djangocms_alias/migrations/0002_auto_20200723_1424.py +40 -0
  28. djangocms-alias-2.0.0rc1/djangocms_alias/migrations/__init__.py +0 -0
  29. djangocms-alias-2.0.0rc1/djangocms_alias/models.py +421 -0
  30. djangocms-alias-2.0.0rc1/djangocms_alias/rendering.py +7 -0
  31. djangocms-alias-2.0.0rc1/djangocms_alias/static/djangocms_alias/js/alias_plugin.js +12 -0
  32. djangocms-alias-2.0.0rc1/djangocms_alias/static/djangocms_alias/js/create.js +91 -0
  33. djangocms-alias-2.0.0rc1/djangocms_alias/static/djangocms_alias/js/dist/bundle.alias.create.min.js +1 -0
  34. djangocms-alias-2.0.0rc1/djangocms_alias/templates/admin/djangocms_alias/alias/change_form.html +10 -0
  35. djangocms-alias-2.0.0rc1/djangocms_alias/templates/admin/djangocms_alias/alias/delete_confirmation.html +46 -0
  36. djangocms-alias-2.0.0rc1/djangocms_alias/templates/admin/djangocms_alias/aliascontent/change_form.html +16 -0
  37. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/add_alias.html +31 -0
  38. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/alias_content.html +1 -0
  39. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/alias_content_preview.html +15 -0
  40. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/alias_recursive.html +0 -0
  41. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/alias_replace.html +41 -0
  42. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/alias_usage.html +55 -0
  43. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/base.html +14 -0
  44. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/create_alias.html +34 -0
  45. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/default/alias.html +1 -0
  46. djangocms-alias-2.0.0rc1/djangocms_alias/templates/djangocms_alias/detach_alias.html +20 -0
  47. djangocms-alias-2.0.0rc1/djangocms_alias/templatetags/__init__.py +0 -0
  48. djangocms-alias-2.0.0rc1/djangocms_alias/templatetags/djangocms_alias_tags.py +184 -0
  49. djangocms-alias-2.0.0rc1/djangocms_alias/test_utils/__init__.py +0 -0
  50. djangocms-alias-2.0.0rc1/djangocms_alias/test_utils/text/__init__.py +0 -0
  51. djangocms-alias-2.0.0rc1/djangocms_alias/test_utils/text/cms_plugins.py +12 -0
  52. djangocms-alias-2.0.0rc1/djangocms_alias/test_utils/text/models.py +10 -0
  53. djangocms-alias-2.0.0rc1/djangocms_alias/urls.py +40 -0
  54. djangocms-alias-2.0.0rc1/djangocms_alias/utils.py +30 -0
  55. djangocms-alias-2.0.0rc1/djangocms_alias/views.py +316 -0
  56. djangocms-alias-2.0.0rc1/djangocms_alias.egg-info/PKG-INFO +112 -0
  57. djangocms-alias-2.0.0rc1/djangocms_alias.egg-info/SOURCES.txt +74 -0
  58. djangocms-alias-2.0.0rc1/djangocms_alias.egg-info/dependency_links.txt +1 -0
  59. djangocms-alias-2.0.0rc1/djangocms_alias.egg-info/requires.txt +3 -0
  60. djangocms-alias-2.0.0rc1/djangocms_alias.egg-info/top_level.txt +2 -0
  61. djangocms-alias-2.0.0rc1/setup.cfg +64 -0
  62. djangocms-alias-2.0.0rc1/setup.py +52 -0
  63. djangocms-alias-2.0.0rc1/tests/__init__.py +0 -0
  64. djangocms-alias-2.0.0rc1/tests/base.py +222 -0
  65. djangocms-alias-2.0.0rc1/tests/test_admin.py +582 -0
  66. djangocms-alias-2.0.0rc1/tests/test_admin_filters.py +302 -0
  67. djangocms-alias-2.0.0rc1/tests/test_cms_config.py +29 -0
  68. djangocms-alias-2.0.0rc1/tests/test_cms_plugins.py +362 -0
  69. djangocms-alias-2.0.0rc1/tests/test_menu.py +26 -0
  70. djangocms-alias-2.0.0rc1/tests/test_models.py +422 -0
  71. djangocms-alias-2.0.0rc1/tests/test_permissions.py +111 -0
  72. djangocms-alias-2.0.0rc1/tests/test_templatetags.py +342 -0
  73. djangocms-alias-2.0.0rc1/tests/test_toolbar.py +457 -0
  74. djangocms-alias-2.0.0rc1/tests/test_views.py +1525 -0
  75. djangocms-alias-2.0.0rc1/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,6 @@
1
+ include LICENSE.txt
2
+ include README.rst
3
+ recursive-include djangocms_alias/static *
4
+ recursive-include djangocms_alias/templates *
5
+ recursive-include djangocms_alias/locale *
6
+ recursive-exclude * *.pyc
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.1
2
+ Name: djangocms-alias
3
+ Version: 2.0.0rc1
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.7
21
+ Classifier: Programming Language :: Python :: 3.8
22
+ Classifier: Programming Language :: Python :: 3.9
23
+ Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Framework :: Django
26
+ Classifier: Framework :: Django :: 3.2
27
+ Classifier: Framework :: Django :: 4.0
28
+ Classifier: Framework :: Django :: 4.1
29
+ Classifier: Framework :: Django CMS :: 4.1
30
+ Description-Content-Type: text/x-rst
31
+ License-File: LICENSE.txt
32
+
33
+
34
+
35
+ ****************
36
+ django CMS Alias
37
+ ****************
38
+
39
+ |coverage| |python| |django| |djangocms4|
40
+
41
+ django CMS Alias replicates and extends the alias function of django CMS version 3 for django CMS version 4.
42
+
43
+ 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.
44
+
45
+ django CMS Alias supports versioning aliases by django CMS Versioning.
46
+
47
+ .. warning::
48
+
49
+ This is the development branch for django CMS version 4.1 support.
50
+
51
+ 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>`_
52
+
53
+
54
+ ============
55
+ Installation
56
+ ============
57
+
58
+ Requirements
59
+ ============
60
+
61
+ django CMS Alias requires that you have a django CMS 4 (or higher) project already running and set up.
62
+
63
+
64
+ To install
65
+ ==========
66
+
67
+ Run::
68
+
69
+ pip install git+https://github.com/django-cms/djangocms-alias@master#egg=djangocms-alias
70
+
71
+ Add ``djangocms_alias`` and ``parler`` to your project's ``INSTALLED_APPS``.
72
+
73
+ Run::
74
+
75
+ python manage.py migrate djangocms_alias
76
+
77
+ to perform the application's database migrations.
78
+
79
+
80
+ =====
81
+ Usage
82
+ =====
83
+
84
+ Static aliases
85
+ ==============
86
+
87
+ Static aliases appear in templates and replace static placeholders which were part of django CMS up to version 3.x.
88
+
89
+ Example::
90
+
91
+ {% load djangocms_alias_tags %}
92
+ ...
93
+ <footer>
94
+ {% static_alias 'footer' %}
95
+ </footer>
96
+
97
+ Alias plugin
98
+ ============
99
+
100
+ 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.
101
+
102
+ .. |coverage| image:: https://codecov.io/gh/django-cms/djangocms-alias/branch/master/graph/badge.svg
103
+ :target: https://codecov.io/gh/django-cms/djangocms-alias
104
+
105
+ .. |python| image:: https://img.shields.io/badge/python-3.7+-blue.svg
106
+ :target: https://pypi.org/project/djangocms-alias/
107
+
108
+ .. |django| image:: https://img.shields.io/badge/django-3.2--4.1-blue.svg
109
+ :target: https://www.djangoproject.com/
110
+
111
+ .. |djangocms4| image:: https://img.shields.io/badge/django%20CMS-4-blue.svg
112
+ :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.0rc1'
@@ -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,7 @@
1
+ from django.apps import AppConfig
2
+ from django.utils.translation import gettext_lazy as _
3
+
4
+
5
+ class AliasConfig(AppConfig):
6
+ name = 'djangocms_alias'
7
+ verbose_name = _('django CMS Alias')
@@ -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,163 @@
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)
144
+ target_placeholder = plugin.placeholder
145
+ source_plugins = plugin.alias.get_plugins(language)
146
+
147
+ # Deleting uses a copy of a plugin to preserve pk on existing
148
+ # ``plugin`` object. This is done due to
149
+ # plugin.get_plugin_toolbar_info requiring a PK in a passed
150
+ # instance.
151
+ source_placeholder.delete_plugin(copy(plugin))
152
+ target_placeholder._shift_plugin_positions(
153
+ language,
154
+ plugin.position,
155
+ offset=target_placeholder.get_last_plugin_position(language),
156
+ )
157
+ copied_plugins = copy_plugins_to_placeholder(
158
+ source_plugins,
159
+ placeholder=target_placeholder,
160
+ language=language,
161
+ start_positions={language: plugin.position},
162
+ )
163
+ return copied_plugins