1- from copy import copy
2-
1+ from cms .models import Page
32from cms .plugin_base import CMSPluginBase , PluginMenuItem
43from cms .plugin_pool import plugin_pool
54from cms .toolbar .utils import get_object_edit_url
5+ from cms .utils import get_language_from_request
66from cms .utils .permissions import (
77 get_model_permission_codename ,
88 has_plugin_permission ,
99)
1010from cms .utils .plugins import copy_plugins_to_placeholder
1111from cms .utils .urlutils import add_url_parameters , admin_reverse
12- from django .utils .translation import (
13- get_language_from_request ,
14- )
12+ from django .core .exceptions import PermissionDenied
13+ from django .http import HttpResponseBadRequest
14+ from django .shortcuts import get_object_or_404
15+ from django .template .response import TemplateResponse
16+ from django .urls import path
1517from django .utils .translation import (
1618 gettext_lazy as _ ,
1719)
1820
21+ from djangocms_alias import constants
22+ from djangocms_alias .utils import emit_content_change
23+
24+ from . import views
1925from .constants import CREATE_ALIAS_URL_NAME , DETACH_ALIAS_PLUGIN_URL_NAME
20- from .forms import AliasPluginForm
26+ from .forms import AliasPluginForm , BaseCreateAliasForm , CreateAliasForm
2127from .models import Alias as AliasModel
2228from .models import AliasContent , AliasPlugin
2329
@@ -40,7 +46,7 @@ def get_render_template(self, context, instance, placeholder):
4046 @classmethod
4147 def get_extra_plugin_menu_items (cls , request , plugin ):
4248 if plugin .plugin_type == cls .__name__ :
43- alias_content = plugin .alias .get_content ()
49+ alias_content = plugin .alias .get_content (show_draft_content = True )
4450 detach_endpoint = admin_reverse (
4551 DETACH_ALIAS_PLUGIN_URL_NAME ,
4652 args = [plugin .pk ],
@@ -74,7 +80,7 @@ def get_extra_plugin_menu_items(cls, request, plugin):
7480
7581 data = {
7682 "plugin" : plugin .pk ,
77- "language" : get_language_from_request (request , check_path = True ),
83+ "language" : get_language_from_request (request ),
7884 }
7985 endpoint = add_url_parameters (admin_reverse (CREATE_ALIAS_URL_NAME ), ** data )
8086 return [
@@ -90,19 +96,20 @@ def get_extra_plugin_menu_items(cls, request, plugin):
9096 def get_extra_placeholder_menu_items (cls , request , placeholder ):
9197 data = {
9298 "placeholder" : placeholder .pk ,
93- "language" : get_language_from_request (request , check_path = True ),
99+ "language" : get_language_from_request (request ),
94100 }
95101 endpoint = add_url_parameters (admin_reverse (CREATE_ALIAS_URL_NAME ), ** data )
96102
97- menu_items = [
98- PluginMenuItem (
99- _ ("Create Alias" ),
100- endpoint ,
101- action = "modal" ,
102- attributes = {"cms-icon" : "alias" },
103- ),
104- ]
105- return menu_items
103+ if placeholder .cmsplugin_set .exists ():
104+ return [
105+ PluginMenuItem (
106+ _ ("Create Alias" ),
107+ endpoint ,
108+ action = "modal" ,
109+ attributes = {"cms-icon" : "alias" },
110+ ),
111+ ]
112+ return []
106113
107114 @classmethod
108115 def can_create_alias (cls , user , plugins = None , replace = False ):
@@ -140,26 +147,169 @@ def can_detach(cls, user, target_placeholder, plugins):
140147
141148 @classmethod
142149 def detach_alias_plugin (cls , plugin , language ):
143- source_placeholder = plugin .alias .get_placeholder (language , show_draft_content = True ) # We're in edit mode
150+ source_plugins = plugin .alias .get_plugins (language , show_draft_content = True ) # We're in edit mode
144151 target_placeholder = plugin .placeholder
152+ plugin_position = plugin .position
153+ target_placeholder .delete_plugin (plugin )
154+ if source_plugins :
155+ if target_last_plugin := target_placeholder .get_last_plugin (plugin .language ):
156+ target_placeholder ._shift_plugin_positions (
157+ language ,
158+ start = plugin_position ,
159+ offset = len (source_plugins ) + target_last_plugin .position + 1 , # enough space to shift back
160+ )
145161
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 (
162+ return copy_plugins_to_placeholder (
159163 source_plugins ,
160164 placeholder = target_placeholder ,
161165 language = language ,
162- start_positions = {language : plugin . position },
166+ start_positions = {language : plugin_position },
163167 )
164- return copied_plugins
165168 return []
169+
170+ def get_plugin_urls (self ):
171+ return super ().get_plugin_urls () + [
172+ path (
173+ "create-alias/" ,
174+ self .create_alias_view ,
175+ name = constants .CREATE_ALIAS_URL_NAME ,
176+ ),
177+ path (
178+ "aliases/<int:pk>/usage/" ,
179+ self .alias_usage_view ,
180+ name = constants .USAGE_ALIAS_URL_NAME ,
181+ ),
182+ path (
183+ "detach-alias/<int:plugin_pk>/" ,
184+ self .detach_alias_plugin_view ,
185+ name = constants .DETACH_ALIAS_PLUGIN_URL_NAME ,
186+ ),
187+ path (
188+ "select2/" ,
189+ views .AliasSelect2View .as_view (),
190+ name = constants .SELECT2_ALIAS_URL_NAME ,
191+ ),
192+ path (
193+ "category-select2/" ,
194+ views .CategorySelect2View .as_view (),
195+ name = constants .CATEGORY_SELECT2_URL_NAME ,
196+ ),
197+ ]
198+
199+ def create_alias_view (self , request ):
200+ if not request .user .is_staff :
201+ raise PermissionDenied
202+
203+ form = BaseCreateAliasForm (request .GET or None )
204+
205+ initial_data = form .cleaned_data if form .is_valid () else None
206+ if request .method == "GET" and not form .is_valid ():
207+ return HttpResponseBadRequest ("Form received unexpected values" )
208+
209+ user = request .user
210+
211+ create_form = CreateAliasForm (
212+ request .POST or None ,
213+ initial = initial_data ,
214+ user = user ,
215+ )
216+
217+ if not create_form .is_valid ():
218+ opts = self .model ._meta
219+ context = {
220+ "form" : create_form ,
221+ "has_change_permission" : True ,
222+ "opts" : opts ,
223+ "root_path" : admin_reverse ("index" ),
224+ "is_popup" : True ,
225+ "app_label" : opts .app_label ,
226+ "media" : (Alias ().media + create_form .media ),
227+ }
228+ return TemplateResponse (request , "djangocms_alias/create_alias.html" , context )
229+
230+ plugins = create_form .get_plugins ()
231+
232+ if not plugins :
233+ return HttpResponseBadRequest (
234+ "Plugins are required to create an alias" ,
235+ )
236+
237+ replace = create_form .cleaned_data .get ("replace" )
238+ if not Alias .can_create_alias (user , plugins , replace ):
239+ raise PermissionDenied
240+
241+ alias , alias_content , alias_plugin = create_form .save ()
242+ emit_content_change ([alias_content ])
243+
244+ if replace :
245+ return self .render_close_frame (
246+ request ,
247+ obj = alias_plugin ,
248+ action = "reload" ,
249+ )
250+ return TemplateResponse (request , "admin/cms/page/close_frame.html" )
251+
252+ def detach_alias_plugin_view (self , request , plugin_pk ):
253+ if not request .user .is_staff :
254+ raise PermissionDenied
255+
256+ instance = get_object_or_404 (AliasPlugin , pk = plugin_pk )
257+
258+ if request .method == "GET" :
259+ opts = self .model ._meta
260+ context = {
261+ "has_change_permission" : True ,
262+ "opts" : opts ,
263+ "root_path" : admin_reverse ("index" ),
264+ "is_popup" : True ,
265+ "app_label" : opts .app_label ,
266+ "object_name" : _ ("Alias" ),
267+ "object" : instance .alias ,
268+ }
269+ return TemplateResponse (request , "djangocms_alias/detach_alias.html" , context )
270+
271+ language = get_language_from_request (request )
272+
273+ plugins = instance .alias .get_plugins (language , show_draft_content = True )
274+ can_detach = self .can_detach (request .user , instance .placeholder , plugins )
275+
276+ if not can_detach :
277+ raise PermissionDenied
278+
279+ self .detach_alias_plugin (
280+ plugin = instance ,
281+ language = language ,
282+ )
283+
284+ return self .render_close_frame (
285+ request ,
286+ obj = instance ,
287+ action = "reload" ,
288+ )
289+
290+ def alias_usage_view (self , request , pk ):
291+ if not request .user .is_staff :
292+ raise PermissionDenied
293+
294+ alias = get_object_or_404 (AliasModel , pk = pk )
295+ opts = AliasModel ._meta
296+ title = _ (f"Objects using alias: { alias } " )
297+ context = {
298+ "has_change_permission" : True ,
299+ "opts" : opts ,
300+ "root_path" : admin_reverse ("index" ),
301+ "is_popup" : True ,
302+ "app_label" : opts .app_label ,
303+ "object_name" : _ ("Alias" ),
304+ "object" : alias ,
305+ "title" : title ,
306+ "original" : title ,
307+ "show_back_btn" : request .GET .get ("back" ),
308+ "objects_list" : sorted (
309+ alias .objects_using ,
310+ # First show Pages on list
311+ key = lambda obj : isinstance (obj , Page ),
312+ reverse = True ,
313+ ),
314+ }
315+ return TemplateResponse (request , "djangocms_alias/alias_usage.html" , context )
0 commit comments