
    Bhz                         d dl Z d dlZd dlmZ d dlmZ d dlmZ d dlmZ d dl	m
Z
 d dlmZ d dlmZ d d	lmZmZ d d
lmZ d dlmZmZmZmZ d dlmZ d dlmZ d dlmZmZ d dl m!Z! d dl"m#Z# d dl$m%Z% d dl&m'Z' d dl(m)Z) d dl*m+Z+ d dl,m-Z-m.Z/ d dl0m1Z1 d dl2m3Z3 d dl4m5Z5 d dl6m7Z7m8Z8m9Z9 d dl:m;Z;m<Z<  G d dejz                        Z>dZ? G d d e      Z@ G d! d"e
      ZA G d# d$e>e      ZB G d% d&      ZC G d' d(      ZDy))    N)copy)	parse_qsl)forms)
ModelAdmin)ModelAdminChecks)label_for_field)
ChangeList)ImproperlyConfiguredValidationError)models)	DateFieldOuterRefSubquery	functions)Cast)modelform_factory)HttpRequestHttpResponse)get_object_or_404render_to_string)NoReverseMatch)format_html_join)	urlencode)	mark_safe)get_languagegettext_lazy)ContentAdminManager)get_object_preview_urlget_language_from_request)get_language_dictget_language_listget_language_tuple)admin_reversestatic_with_versionc                       e Zd ZdZ G d d      Z ed      Zdeej                  e
j                  egef      fdZdedej                  e
j                  gef   fdZd	e
j                  dd
fdZdedeej$                  eej                  e
j                  gef   f   df   f fdZe	 	 	 	 	 ddededededededededefd       Z xZS )ChangeListActionsMixinaw  ChangeListActionsMixin is a mixin for the ModelAdmin class. It adds the ability to have
    action buttons and a burger menu in the admin's change list view. Unlike actions that affect
    multiple listed items the list action buttons only affect one item at a time.

    Use :meth:`~cms.admin.utils.ChangeListActionsMixin.get_action_list` to register actions and
    :meth:`~cms.admin.utils.ChangeListActionsMixin.admin_action_button` to define the button
    behavior.

    To activate the actions make sure ``"admin_list_actions"`` is in the admin classes
    :prop:`~django.contrib.admin.ModelAdmin.list_display` property.
    c                   &    e Zd ZdZd ed      fiZy)ChangeListActionsMixin.Media)admin/js/jquery.init.jszcms/js/admin/actions.jsallzcms/css/cms.admin.cssN)__name__
__module____qualname__jsr&   css     ?/home/dcms/DCMS/lib/python3.12/site-packages/cms/admin/utils.pyMediar*   -   s    
 *+BCEFr3   r5   z&<span class="cms-empty-action"></span>returnc                     g S )a  Collect list actions from implemented methods and return as list. Make sure to call
        it's ``super()`` instance when overwriting::

            class MyModelAdmin(admin.ModelAdmin):
                ...

                def get_actions_list(self):
                    return super().get_actions_list() + [
                        self.my_first_action,
                        self.my_second_action,
                    ]
        r2   selfs    r4   get_actions_listz'ChangeListActionsMixin.get_actions_list6   s	     	r3   requestc                 d     dt         j                  dt        f fd}t        d      |_        |S )a  Method to register the admin action menu with the admin's list display

        Usage (in your model admin)::

            class MyModelAdmin(AdminActionsMixin, admin.ModelAdmin):
                ...
                list_display = ("name", ..., "admin_list_actions")

        objr6   c                 P     t        dd fdj                         D              S )zRThe name of this inner function must not change. css styling and js depends on it. z{}c              3   2   K   | ]  } |      f  y wNr2   ).0actionr=   r;   s     r4   	<genexpr>zVChangeListActionsMixin.get_admin_list_actions.<locals>.list_actions.<locals>.<genexpr>W   s     OV&g&(Os   )r   r:   )r=   r;   r9   s   `r4   list_actionszCChangeListActionsMixin.get_admin_list_actions.<locals>.list_actionsR   s'    #Ot7L7L7NO r3   Actions)r   Modelstr_short_description)r9   r;   rE   s   `` r4   get_admin_list_actionsz-ChangeListActionsMixin.get_admin_list_actionsG   s.    	fll 	s 	 *+9&r3   r=   Nc                     t        d      )NzModelAdmin.display_list contains "admin_list_actions" as a placeholder for list action icons. ChangeListActionsMixin is not loaded, however. If you implement "get_list_display" make sure it calls super().get_list_display.)
ValueErrorr9   r=   s     r4   admin_list_actionsz)ChangeListActionsMixin.admin_list_actions]   s    6
 	
r3   .c                 P     t                  }t         fd|D              S )Nc              3   N   K   | ]  }|d k(  rj                        n|  yw)rO   N)rK   )rB   itemr;   r9   s     r4   rD   z:ChangeListActionsMixin.get_list_display.<locals>.<genexpr>h   s/      
_cD<P4PD''0VZZ
s   "%)superget_list_displaytuple)r9   r;   list_display	__class__s   `` r4   rT   z'ChangeListActionsMixin.get_list_displayd   s.     w/8 
gs
 
 	
r3   urlicontitleburger_menurC   disabledkeepsideframenamec                 4    t        d| xs d|||||||d      S )a  Returns a generic button supported by the ChangeListActionsMixin.

        :param str url:  Url of the action as string, typically generated by :func:`~cms.utils.urlutils.admin_reverse`_
        :param str icon: Name of the icon shown in the button or before the title in the burger menu.
        :param str title: Human-readable string describing the action.
        :param bool burger_menu: If ``True`` the action item will be part of a burger menu right og all buttons.
        :param str action: Either ``"get"`` or ``"post"`` defining the html method used for the url. Some urls
                           require a post method.
        :param bool disabled: If ``True`` the item is grayed out and cannot be selected.
        :param bool keepsideframe:  If ``False`` the side frame (if open) will be closed before executing the action.
        :param str name: A string that will be added to the class list of the button/menu item:
                         ``cms-action-{{ name }}``

        To add an action button to the change list use the following pattern in your admin class::

                 def my_custom_button(self, obj, request, disabled=False):
                     # do preparations, e.g., check permissions, get url, ...
                     url = admin_reverse("...", args=[obj.pk])
                     if permissions_ok:
                         return self.admin_action_button(
                             url, "info", _("View usage"), disabled=disabled
                         )
                     return ""  # No button

        zadmin/cms/icons/base.htmlr?   )rX   rY   methodr\   r]   rZ   r[   r^   r   )rX   rY   rZ   r[   rC   r\   r]   r^   s           r4   admin_action_buttonz*ChangeListActionsMixin.admin_action_buttonl   s6    H  'yb $!.*	
 	
r3   )FgetFTr?   )r-   r.   r/   __doc__r5   r   EMPTY_ACTIONlisttypingCallabler   rG   r   rH   r:   rK   rO   rU   UnionrT   staticmethodboolra   __classcell__rW   s   @r4   r(   r(       sE   
G G EFL	foov||[93>?	@"k foov||n^aNa>b ,
fll 
t 

"
	v||C&,,1D!EEFK	L
 
 ""/
/
/
 /
 	/

 /
 /
 /
 /
 
/
 /
r3   r(   )	metaclass	content__c                   H     e Zd ZdZg Zddej                  e   f fdZ xZ	S )GrouperChangeListBasezHSubclass ChangeList to disregard grouping fields get parameter as filterparamsc                 X    t         |   |      }| j                  D ]
  }||v s||=  |S rA   )rS   get_filters_params_extra_grouping_fields)r9   rq   lookup_paramsfieldrW   s       r4   rs   z(GrouperChangeListBase.get_filters_params   s?    26:00 	)E%!%(	) r3   rA   )
r-   r.   r/   rc   rt   rf   Optionaldictrs   rk   rl   s   @r4   rp   rp      s&    R)>  r3   rp   c                   (     e Zd Z fdZ fdZ xZS )GrouperModelAdminChecksc                     |j                  t              r:|j                  r.|t        t              d }t	        |      }|j                  |_        t        |   |||      S )zkFor `prepopulated_fields` equal to {"slug": ("content__title",)},
        `field_name` is "content__title".N)
startswithCONTENT_PREFIXcontent_modellenr   modelrS   %_check_prepopulated_fields_value_itemr9   r=   
field_namelabelrW   s       r4   r   z=GrouperModelAdminChecks._check_prepopulated_fields_value_item   sW       0S5F5F#C$7$9:Js)C))CIw<S*eTTr3   c                     |j                  t              r:|j                  r.|t        t              d }t	        |      }|j                  |_        t        |   |||      S )zCheck a key of `prepopulated_fields` dictionary, i.e. check that it
        is a name of existing field and the field is one of the allowed types.
        N)r|   r}   r~   r   r   r   rS   _check_prepopulated_fields_keyr   s       r4   r   z6GrouperModelAdminChecks._check_prepopulated_fields_key   sW    
   0S5F5F#C$7$9:Js)C))CIw5c:uMMr3   )r-   r.   r/   r   r   rk   rl   s   @r4   rz   rz      s    U	N 	Nr3   rz   c                       e Zd ZU dZdZej                  e   ed<   dZ	e
edf   ed<   dZej                  ej                     ed<   dZej                  e   ed<   d	Zd
ZeZ G d d      Z ed      Zej,                  fZdZdZ fdZdedej6                  ej                  gej8                  f   fdZ	 d<dej                  dedej                  e   dej8                  fdZd Z dedejB                  f fdZ"dedefdZ#deddfdZ$e%de&eej8                  f   fd       Z'defdZ(de
e
eef   df   fdZ)d Z*dede+fdZ,dede-f fdZ.	 	 	 d=ded ej                  e   d!ed"e&de/f
 fd#Z0	 d<ded ed"ej                  e&   de/f fd$Z1	 d<ded ed"ej                  e&   de/f fd%Z2dedef fd&Z3d<ded ej                  e   de&eej8                  f   fd'Z4d<dedej                  ej                     de+f fd(Z5dedefd)Z6dej                  dedefd*Z7de8fd+Z9d,edej                  defd-Z:dej                  de;fd.Z<dej                  dejB                  fd/Z=dej                  ej                     dej                  ej                     fd0Z>dej                  ej                     dejB                  fd1Z?d>d2Z@dej                  dej                  fd3ZAdej                  dej                  e   fd4ZBd<dedej                  ej                     f fd5ZCdedej                  d6eDj                  d7e;ddf
 fd8ZFd9 ZG fd:ZH fd;ZI xZJS )?GrouperModelAdmina  Easy-to-use ModelAdmin for grouper models. Usage example::

        class MyGrouperAdmin(GrouperModelAdmin):
            # Add language tabs to change and add views
            extra_grouping_fields = ("language",)
            # Add grouper and content fields to change list view
            # Add preview and settings action to change list view
            list_display = ("field_in_grouper_model", "content__field_in_content_model", "admin_list_actions")

            # Automatically add content fields to change form (either the standard form or any form given
            form = MyChangeForm

            ...

    Using ``GrouperModelAdmin`` instead of :class:`~django.contrib.admin.ModelAdmin` adds a view standard functions
    to your admin class to make it more easily and more consistently customizable.

    1. By adding ``"admin_list_actions"`` to the admin's :attr:`~django.contrib.admin.ModelAdmin.list_display`
        attribute the change list view gets an action column as described by
        :class:`~cms.admin.utils.ChangeListActionsMixin`.
    2. The admin class automatically creates a method for each field of the content model form (default: all fields)
        named ``content__{content_model_field_name}``. Those fields can be used in
        :attr:`~django.contrib.admin.ModelAdmin.list_display` just as grouper model fields.
        Currently, they are not sortable, however.
    3. The change form is amended with exactly those content fields also named ``content__{content_model_field_name}``.
        As a result, the change form can (but does not have to) contain both grouper model fields and content model
        fields. The admin takes care of creating the necessary model instances.
    Ngrouper_field_namer2   .extra_grouping_fieldsr~   content_related_fieldz"admin/cms/grouper/change_list.htmlz"admin/cms/grouper/change_form.htmlc                       e Zd ZdZy)GrouperModelAdmin.Media)r+   z!cms/js/admin/language-selector.jsN)r-   r.   r/   r0   r2   r3   r4   r5   r      s	    
r3   r5   zEmpty contentc                    g | _         i | _        i | _        t        |   ||       | j
                  Jddlm} |j                  | j                  j                   d| j                  j                   d      | _        t        | j
                  d      s$| j
                  j                  dt                      | j                   sc|j"                  j$                  D ]1  }|j&                  | j
                  u s|j)                         | _         n t+        d|j                   d      | j,                  s>t/        j0                  dd	| j                  j                        j3                         | _        t5        | j6                  t8              s9t;        d
t=        | j
                        | j6                  ft?                     | _        | j6                  j@                  D ]  }t        | tB        |z         r|| j,                  k7  s'|| jD                  vs6tB        |z   | jF                  v r| j                   jI                  |       tK        | tB        |z   | jM                  |              y )Nr   )apps.Contentadmin_managerz Related field for grouper model z
 not foundz(?!^)([A-Z]+)z_\1AutoGeneratedGrouperAdminForm)'_content_subquery_fields_content_obj_cache_content_qs_cacherS   __init__r~   django.appsr   	get_modelopts	app_labelr   r-   hasattradd_to_classr   r   _metarelated_objectsrelated_modelget_accessor_namer
   r   resublower
issubclassform_GrouperAdminFormMixintypeGrouperAdminFormMixinrx   _content_fieldsr}   r   rV   appendsetattr_getter_factory)r9   r   
admin_siter   related_objectcontent_fieldrW   s         r4   r   zGrouperModelAdmin.__init__  s   (*%"$!#
+ %(!%4993F3F2GqI\I\H]]d0e!fD t))?;++O=P=RS ))"'++"="= j!//43E3EE1?1Q1Q1SD.j
 +-MennM]]g+hii &&&(ff_fdjjFYFY&Z&`&`&bD#$))%;</&t'9'9:DIIFDI "YY66 	MD.="@A!T%<%<<!)C)CC!M1T5F5FF1188G"]2((7	r3   rv   r6   c                 @     fd}t         j                        |_         j                  v r\t        z   |_        t         j                  j                  j                         j                        r|xj
                  dz  c_        t         j                  j                  t        z      t        j                        |_        |j                  sGt         dd      D ]7  }|t        z   k(  r j                   |_        |j%                  t              s6 |S  |S )zCreates a getter function with ``short_description``, ``admin_order_field``, and ``boolean``
        properties suitable for the :attr:`~django.contrib.admin.ModelAdmin.list_display` field.c                 (    j                  |       S rA   )get_content_field)r=   rv   r9   s    r4   getterz1GrouperModelAdmin._getter_factory.<locals>.getterF  s    ))#u55r3   __lcrV   r2   )r   r~   rJ   r   r}   admin_order_field
isinstancer   	get_fieldLC_SORTED_FIELDSr   base_fieldsr   BooleanFieldbooleangetattrEMPTY_CONTENT_VALUEempty_value_displayr|   )r9   rv   r   displays   ``  r4   r   z!GrouperModelAdmin._getter_factoryB  s    	6 $35$:L:L#M D111'5'=F$$,,22<<UCTEZEZ[((F2(#DII$9$9.5:P$QSXSeSef~~"4< nu44151I1IF.%%n5
 r3   r=   r   r;   c                     t        |t        |z         rt        |t        |z         S |r| j                  |       | j	                  |      }|rt        ||      S dS )zRetrieves the content of a field stored in the content model. If request is given extra
        grouping fields are processed before.N)r   r}   r   get_grouping_from_requestget_content_obj)r9   r=   r   r;   content_objs        r4   r   z#GrouperModelAdmin.get_content_fieldX  sZ     3343 ;<<**73**3/3>w{J/HDHr3   c                 v    | j                   j                  j                  di | j                  t	        d      i| j
                  }i }| j                  D ]  }t        |j                  |      d d       |t        |z   <   | j                   j                  j                  |      }t        |t              r.t        |t        |z      |j                               |t        |z   <   t        || j                         st#        j$                  t        |j                  |      d d             |t        |z   dz   <    |S )Npk   r   r2   )r~   r   latest_contentr   r   current_content_filtersr   r   valuesr}   r   r   r   r   r   rW   r   r   Lower)r9   contents
annotationr   rv   s        r4   _get_annotationz!GrouperModelAdmin._get_annotationh  s$   B4%%33BB 
&&W$:V:VW
 
77 	J6>xz?Z[]\]?^6_J~
23&&,,66zBE%+:>~
:;U__=N;
>J67 %!6!67CL??X__Z8!<=D
>J6?@	 r3   c                 \    t        |   |      j                  di | j                         S )zAnnotates content fields with the name "content__{field_name}" to the grouper queryset if
        for all content fields that appear in ther2   )rS   get_querysetannotater   r9   r;   rW   s     r4   r   zGrouperModelAdmin.get_queryset|  s,     w#G,55O8L8L8NOOr3   c                     t        |      S )zHHook for get_language_from_request which by default uses the cms utilityr    )r9   r;   s     r4   r!   z+GrouperModelAdmin.get_language_from_request  s    (11r3   c                    t        |      | j                  k7  r t        |      | _        | j                          | j                  D ]x  }t	        | d| d      r t        | d| d      |      }n1t        | j                  j                   d| d| j                         |t        | |d      k7  slt        | ||       z y)z9Retrieves the current grouping selectors from the requestget__from_requestz lacks method 'get_z;_from_request(request)' to work with extra_grouping_fields=N)
hash_content_cache_request_hashclear_content_cacher   r   r   r
   rW   r-   r   )r9   r;   rv   values       r4   r   z+GrouperModelAdmin.get_grouping_from_request  s    =D<<</3G}D,$$&// 		,EttE7-89BUG=&AB7K*~~..//B5' J--1-G-G,HJ  eT22eU+		,r3   c                 v    | j                   D ci c]  }|t        | || j                  |            ! c}S c c}w )z8Filters needed to get the correct content model instance)r   r   get_extra_grouping_fieldr9   rv   s     r4   r   z)GrouperModelAdmin.current_content_filters  sB     \`[u[u
RWE74(E(Ee(LMM
 	
 
s   $6c                 ,    t        | dt                     S )zHook on how to get the current language. By default, if it is set as a
        property, use the property, otherwise let Django provide it.language)r   r   r8   s    r4   r   zGrouperModelAdmin.get_language  s     tZ88r3   c                     t               S )zEHook on how to get all available languages for the language selector.)r$   r8   s    r4   r$   z$GrouperModelAdmin.get_language_tuple  s    !##r3   c                 r    t        t        | d| d            r t        | d|              S t        d      )zRetrieves the current value for grouping fields - by default by calling self.get_<field>, e.g.,
        self.get_language(). If those are not implemented, this method will fail.r   NzCannot get extra grouping field)callabler   rM   r   s     r4   r   z*GrouperModelAdmin.get_extra_grouping_field  s?     GDD.$780744w022:;;r3   c                 j    t        t        j                  t        ft        | j                              S )z9Allow for extra grouping fields as a non-filter parameter)rt   )r   rp   r-   rx   r   )r9   r;   kwargss      r4   get_changelistz GrouperModelAdmin.get_changelist  s,    !**"$(B(BC
 	
r3   c                 D    | j                  |       t        | 	  |      S )z<Update grouping field properties and get changelist instance)r   rS   get_changelist_instancer   s     r4   r   z)GrouperModelAdmin.get_changelist_instance  s!    &&w/w.w77r3   	object_idform_urlextra_contextc           
      |    | j                  |       t        | 	  |||i |xs i | j                  ||            S )z>Update grouping field properties for both add and change views)r   )r   rS   changeform_viewget_extra_context)r9   r;   r   r   r   rW   s        r4   r   z!GrouperModelAdmin.changeform_view  sX     	&&w/w& &B((I(F	
 	
r3   c                 H    | j                  |       t        | 	  |||      S )z0Update grouping field properties for delete view)r   rS   delete_viewr9   r;   r   r   rW   s       r4   r   zGrouperModelAdmin.delete_view  s'     	&&w/w"7I}EEr3   c                 H    | j                  |       t        | 	  |||      S )z1Update grouping field properties for history view)r   rS   history_viewr   s       r4   r   zGrouperModelAdmin.history_view  s'     	&&w/w#GYFFr3   c                     t        t        t        |   |                  }i }| j                  D ]  }t        | |d      }d|vs|||<    |j                  |       d|vrt        |      |d<   t        |      S )zAlways preserve grouping get parameters! Also, add them to changelist filters:
        * Save and continue will keep the grouping parameters
        * Save and returning to changelist will keep the grouping parameters
        Nrv   _changelist_filters)rx   r   rS   get_preserved_filtersr   r   updater   )r9   r;   preserved_filtersgrouping_filtersrv   r   rW   s         r4   r   z'GrouperModelAdmin.get_preserved_filters  s    
 !57+H+Q!RS// 	0ED%.E//*/ '	0 	  !12 (997@AQ7R34*++r3   c                    |rct        | j                  |      }| j                  |      }t        d      t	        |j
                  j                  j                               z  }n:d}d}t        d      t	        | j                  j
                  j                        z  }|rt        |      }nt        d      }t        d      |||d}	 d	| j                  v r| j                  }|r1| j                  |      j                  d	d
      j                         }	ng }	| j                         |d<   ||d	<   |	|d<   |>t        d      t	        t               j!                  | j                              z  }||d<   |S )z/Provide the grouping fields to the change view.)r   z%(object_name)s Properties)object_nameNzAdd new %(object_name)sAdd contentzContent for the current language has been changed. Click "Cancel" to return to the form and save changes. Click "OK" to discard changes.)changed_messagerZ   content_instancesubtitler   Tflatlanguage_tabsfilled_languageszAdd %(language)s contentr   r   )r   r   r   rI   rx   r   verbose_name
capitalizerH   r   r   get_content_objectsvalues_listdistinctr$   r"   rb   )
r9   r;   r   r=   r   rZ   r   r   r   r  s
             r4   r   z#GrouperModelAdmin.get_extra_context  ss   #DJJ9=C#33C823dsyyG]G]GhGhGj6kkEC#/04DJJDTDTDaDa3bbE+,H'H  !V   0 
 	2333}}H#'#;#;C#@#L#LZ^b#L#c#l#l#n #% -1-D-D-FM/*(0M*%0@M,-'784IZI\I`I`aeananIo;pp,4j) r3   c           	         t        |   ||fi |}| |_        ||_        | j                  D ]/  }t        j                         |j                  t        |z      _	        1 t        |j                  dd      xs ddk7  rb| j                  D ]S  }t        |z   |j                  j                  vs#t        | j                  j                   d| j                   d| d       |S )z4Adds the language from the request to the form classfieldsN__all__z, needs to include all extra_grouping_fields=z in its admin. z is missing.)rS   get_form_admin_requestr   r   HiddenInputr   r}   widgetr   r   r
  r
   rW   r-   )r9   r;   r=   r   
form_classrv   rW   s         r4   r  zGrouperModelAdmin.get_form  s    W%gs=f=
 
%
// 	XEDIDUDUDWJ"">E#9:A	X J$$h5ByP33 !E)1A1A1H1HH.>>223 41151K1K0LO\a[bbnp  r3   c                     | j                  |      rA| j                  | j                  |            }| j                  |dt        d      | dd      S | j                  S )NviewPreviewF)rX   rY   rZ   r\   r]   r^   )r   view_on_sitera   rI   rd   )r9   r=   r;   view_urls       r4   _get_view_actionz"GrouperModelAdmin._get_view_action4  si    $(()=)=c)BCH++	l%# ,      r3   c                 b   t        |j                  j                   d|j                  j                   d|j                  f      }|dt        | j                         z  }| j                  || j                  |      rdnd| j                  |      rt        d      n
t        d      | d	      S )
NrI   _changeargs?settingsplusSettingsr   )rX   rY   rZ   r\   r^   )
r%   r   r   
model_namer   r   r   ra   r   rI   )r9   r=   r;   edit_urls       r4   _get_settings_actionz&GrouperModelAdmin._get_settings_actionA  s     CII$7$7#8#)):N:N9Ow!W_b_e_e^gha	$">">?@AA''#33C8f#'#7#7#<!J-!MBR!\ ( 
 	
r3   c                 2    | j                   | j                  gS rA   )r  r"  r8   s    r4   r:   z"GrouperModelAdmin.get_actions_listL  s    %%t'@'@AAr3   adminc                 r   | j                  |      r|j                  }|j                  }n)| j                  |      }|j                  }|j                  }| j                  0ddlm} |j                  j                  |      j                  | _        	 t        || j                  |g      S # t        $ r Y yw xY w)Nr   )ContentTyper  r?   )_is_content_objrW   r   r   _content_content_type"django.contrib.contenttypes.modelsr&  objectsget_for_modelr%   r   )r9   r$  r=   clsr   contentr&  s          r4   endpoint_urlzGrouperModelAdmin.endpoint_urlO  s    $--CB**3/G##CB%%-F)4)<)<)J)J3)O)R)RD&	 d.H.H"-MNN 		s   B* *	B65B6c                 .    t        || j                        S rA   )r   r~   rN   s     r4   r'  z!GrouperModelAdmin._is_content_obja  s    #t1122r3   c                     || j                   vr8 t        || j                        d      j                         | j                   |<   | j                   |   S )Nr   )manager)r   r   r   r   rN   s     r4   _get_content_querysetz'GrouperModelAdmin._get_content_querysetd  sU    d,,,*R'#t7Q7Q*R'+n ""3' %%c**r3   c                     || j                  |      r|S || j                  vrF | j                  |      j                  di | j                  j                         | j                  |<   | j                  |   S )Nr2   )r'  r   r2  filterr   firstrN   s     r4   r   z!GrouperModelAdmin.get_content_objk  sw    ;$..s3J$111:D..s3::ZT=Y=YZ``b '', **3//r3   c                     |y | j                  |      r | j                  | j                  |            S | j                  |      S rA   )r'  r  get_grouper_objr2  rN   s     r4   r  z%GrouperModelAdmin.get_content_objectsu  sE    ;$++D,@,@,EFF))#..r3   c                      i | _         i | _        y)z$Clear cache, e.g., for a new requestN)r   r   r8   s    r4   r   z%GrouperModelAdmin.clear_content_cache}  s    "$!#r3   c                     | j                  |      r3|j                  j                  dd j                         }t	        ||      S |S )zGet the admin object. If obj is a content object assume that the admin object
        resides in the field named after the admin model. The admin model name must be
        the same as the content model name minus "Content" at the end.iN)r'  rW   r-   r   r   )r9   r=   r   s      r4   r7  z!GrouperModelAdmin.get_grouper_obj  sC     $//4::<J3
++
r3   c                 Z    | j                  |      }|rt        |t        |dd             S y )Nr   r  )r   r   r   )r9   r=   r   s      r4   r  zGrouperModelAdmin.view_on_site  s0    **3/)+U_ae@fggr3   c                      t            ||      }t         d      rN j                  |      } j	                  ||      s+|t         fd j                  j                  D              z  }|S )zAllow access to content fields to be controlled by a method "can_change_content":
        This allows versioned content to be protected if neededcan_change_contentc              3   j   K   | ]*  }|j                   k7  r|j                  vrt        |z    , y wrA   )r   r   r}   )rB   rv   r9   s     r4   rD   z8GrouperModelAdmin.get_readonly_fields.<locals>.<genexpr>  s9        7 77EIcIc<c #U* s   03)rS   get_readonly_fieldsr   r   r<  rU   r   r   )r9   r;   r=   r
  r   rW   s   `    r4   r>  z%GrouperModelAdmin.get_readonly_fields  sp     ,Wc:4-...s3K**7K@%  !%!:!:   
 r3   r   changec                    t         	|   ||xs |j                  ||       |j                  D ci c].  }t        |z   |j
                  v r||j
                  t        |z      0 }}|j                  |j                  j                  |j                  || j                  <   t        |j                  j                  d      r@ |j                  j                  j                  |j                        j                  di | y |j                  j                  j                  di | yt        | d      r| j                  ||j                        rk|j!                         D ]  \  }}t#        |j                  ||        t#        |j                  | j                  |       |j                  j%                          yyc c}w )z+Save/create both grouper and content objectN	with_userr<  r2   )rS   
save_modelinstancer   r}   cleaned_data_content_instancer   r   r   _content_modelr*  rA  usercreater<  itemsr   save)
r9   r;   r=   r   r?  rv   content_dictkeyr   rW   s
            r4   rB  zGrouperModelAdmin.save_model  s   7C$84==$G --
%):):: 4$$^e%;<<
 

 !!)T-C-C-F-F-N48MML001t**22K@J##++55gllCJJZ\Z 3##++22B\B348O8OPWY]YoYo8p*002 <
U..U;< D**D,C,CSI""'') 9q
s   3F;c                     g }g }| j                   D ]J  }|j                  t              r"|j                  |t	        t              d        :|j                  |       L t        |dd      r|S |S )z>Return search fields for either grouper model or content modelNr   F)search_fieldsr|   r}   r   r   r   )r9   r;   content_search_fieldsgrouper_search_fieldsr   s        r4   get_search_fieldsz#GrouperModelAdmin.get_search_fields  su     " ",, 	9J$$^4%,,ZN8K8M-NO%,,Z8		9 7-u5(($$r3   c                 l    t         |   |||      \  }}| j                  |||      \  }}||z  ||z  fS rA   )rS   get_search_results_get_content_search_result)	r9   r;   querysetsearch_termgrouper_search_resultmay_have_duplicate_groupersearch_result_from_contentmay_have_duplicate_contentrW   s	           r4   rS  z$GrouperModelAdmin.get_search_results  s]    <AG<VW^`hju<v99AEA`A`X{B
>"$> %'AA&)CC
 	
r3   c                 ~   	 d|_         | j                  j                  j                         }| j	                  |      rt
        |   |||      \  }}n$| j                  j                  j                         }|j                  |j                  | j                   dd            }d|_         |dfS # d|_         w xY w)z%Get search results from content modelT_idr   )id__inF)r   r~   r   r,   rQ  rS   rS  noner4  r  r   )	r9   r;   rU  rV  content_querysetcontent_search_result__rY  rW   s	           r4   rT  z,GrouperModelAdmin._get_content_search_result  s    	, '+G##11??CCE%%g.,1G,FwP`bm,n)%r(,(:(:(H(H(M(M(O%)1,88D<S<S;TTW9X_c8d *9 *& ',G#)500 ',G#s   B%B3 3	B<rA   )Nr?   N)r6   N)Kr-   r.   r/   rc   r   rf   rw   rH   __annotations__r   rU   r~   r   rG   r   change_list_templatechange_form_templaterz   checks_classr5   rI   r   	CharFieldr   r   r(  r   rg   Anyr   r   r   r   QuerySetr   r!   r   propertyrx   r   r   r$   r   r   r   rp   r   r   r   r   r   r   r   r  r  r"  re   r:   r.  rj   r'  r2  r   r  r   r7  r  r>  r   FormrB  rQ  rS  rT  rk   rl   s   @r4   r   r      s   @ 04,3 .05c?/ 48M6??6<<07
 376??3/6??*L
 
 O,((*"& 4lS V__fll^VZZ=W-X 4 15	I\\I I -	I
 
I (PK PFOO P
2 2 2, , ,  
c6::o)> 
 
9c 9
$E%S/3*>$? $<
k 
 
8{ 8?T 8 +/"

 ??3'
 	

 
 

. 04	FF F t,	F
 
F 04	GG G t,	G
 
G,[ ,S ,"+ +QTAU +aefikqkukufuav +Z &//&,,2O ei ,![ !S !	
 	
{ 	
s 	
B$ B# FLL S $36<< 3D 3+ +&// +06??6<<#@ 0V__U[UaUaEb 0/vv||'D / /$
6<< FLL  1E ; V__V\\=Z  *+ *FLL *

 *\` *ei *0%	
1 1r3   r   c                   T     e Zd ZU g Zeed<    fdZdee   ddfdZde	f fdZ
 xZS )r   r   c           	      r   t        | d      st        d      d|v r|d   r|d   }| j                  j                  |      | _        | j                  rji | j
                  D ci c]5  }t        |z   | j                  v rt        |z   t        | j                  |      7 c}|j                  di       |d<   nd | _        i | j                  j                  j                         D ci c]  \  }}t        |z   | c}}|j                  di       |d<   t        | 4  |i | t        j                         | j                   t        | j                  j"                  z      _        d| j                   t        | j                  j"                  z      _        | j)                  | j
                         y c c}w c c}}w )Nr  zGrouperModelFormMixin forms can only be instantiated if the class attribute '_admin' has been set and points to the instantiating admin instance.rC  initialF)r   rM   r  r   rE  r   r}   r   r   rb   r   rI  rS   r   r   r  r
  r   r  requiredupdate_labels)r9   r  r   rC  rv   rL  r   rW   s          r4   r   z_GrouperAdminFormMixin.__init__  s   tX&O 
 F:$6j)H%)[[%@%@%JD"%%% &*%9%9!)E1T5E5EE '.8N8NPU0VV% jjB/%y! &*D"
=A[[=`=`=f=f=hizsE~#U*i
jjB'
y 	$)&) OTN_N_NaNT[[%C%CCDKPUNT[[%C%CCDM4//0/ js   ":F.%F3r
  r6   Nc                    d| j                   j                  v rt               }d|| j                   j                      d}|D ]  }t        |z   | j
                  v r*| j
                  t        |z      xj                  |z  c_        B| j                  j                  i | j                  _        | j                  j                  j                  t        |z   t        || j                   j                        |z           yy)z)Adds a language indicator to field labelsr   z ()N)r  r   r"   r   r}   r
  r   r   labels
setdefaultr   r~   )r9   r
  language_dictlanguage_postfixrv   s        r4   ro  z$_GrouperAdminFormMixin.update_labels  s    :::-/M!#M$++2F2F$G#HJ !E)T[[8KK 67==AQQ= zz((0,.

)JJ%%00&.'t{{/H/HIL\\ ;r3   c                    t          d| j                  v rd| j                  t          d   t               vrDt        t	        d      t        | j                  j                  dt	        d                  d      t        | !         S )Nr   zRInvalid language %(value)s. This form cannot be processed. Try changing languages.z<unspecified>)r   zinvalid-language)rq   code)	r}   rD  r#   r   rI   rx   rb   rS   clean)r9   rW   s    r4   rx  z_GrouperAdminFormMixin.clean#  s    h'4+<+<<!!^$4H"=>FWFYY!fg$"3"3"7"7
AoDV"WX' 
 w}r3   )r-   r.   r/   r   re   rb  r   rH   ro  rx   rx  rk   rl   s   @r4   r   r     s;    OT$1LDI $ $
t 
 
r3   r   c                   H    e Zd ZdZdej
                  j                  defdZy)r   a  Actually a factory class that creates the GrouperAdminFormMixin. Pass the Model or ModelForm as a
    parameter::

        class MyGrouperModelForm(GrouperModelFormMixin(ContentModel), forms.ModelForm):
            model = GrouperModel
            ...

    .. info::

        For most cases you will not need to use this mixin. :class:`~cms.admin.utils.GrouperModelAdmin` automatically
        adds the mixin to the form provided to it or the standard :class:`~django.forms.ModelForm`. As a results, you
        can just use a subclass of :class:`~django.forms.ModelForm` for :class:`~cms.admin.utils.GrouperModelAdmin`.

    .. warning::

        This mixin only works when used together with :class:`~cms.admin.utils.GrouperModelAdmin`.
    r~   r6   c                 ^   t        |d      }|j                  j                         D ci c]  \  }}t        |z   | }}}t        j                  j                  t        j                  t        fi ||j                  j                  |j                  j                         d      S c c}}w )Nr  )r
  )rF  r   )r   r   rI  r}   r   DeclarativeFieldsMetaclassr   r-   r   r   r   keys)r,  r~   
model_formrL  r   r   s         r4   __new__zGrouperAdminFormMixin.__new__C  s    &}YG
EOE[E[EaEaEcdzsE~+U2dd{{55!**#%","2"2"8"8#-#9#9#>#>#@
 	
 es   B)N)	r-   r.   r/   rc   r   base	ModelBaser   r~  r2   r3   r4   r   r   0  s$    $
FKK$9$9 
d 
r3   r   )Er   rf   r   urllib.parser   djangor   django.contrib.adminr   django.contrib.admin.checksr   django.contrib.admin.utilsr   django.contrib.admin.views.mainr	   django.core.exceptionsr
   r   	django.dbr   django.db.modelsr   r   r   r   django.db.models.functionsr   django.formsr   django.httpr   r   django.shortcutsr   django.template.loaderr   django.urlsr   django.utils.htmlr   django.utils.httpr   django.utils.safestringr   django.utils.translationr   r   rI   cms.models.managersr   cms.toolbar.utilsr   	cms.utilsr!   cms.utils.i18nr"   r#   r$   cms.utils.urlutilsr%   r&   MediaDefiningClassr(   r}   rp   rz   r   r   r   r2   r3   r4   <module>r     s    	   "  + 8 6 6 H  E E + * 1 . 3 & . ' - D 3 4 / S S A|
u'?'? |
@ 
J 
N. N.^1.
 ^1BE EP
 
r3   