a
    DgO                     @   s  d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlmZ d dl	m
Z
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 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* d dl+m,Z,m-Z- d dl.m/Z/m0Z0 d dl.m1Z2 d dl3m4Z4 e
rVd dl5m6Z6 e7e8Z9e:eddZ;dd Z<dd Z=dd  Z>dPd!d"Z?e@d#ZAd$d% ZBe@d&ejCZDd'd( ZEd)d* ZFd+d, ZGd-d. ZHG d/d0 d0ZIdQd1d2ZJe jd3d4 ZKe jLd5d6dRd8d9ZMeNd:d;d<ZOe$ed=d> ZPd?d@ ZQdAddBeRdCe&dDdEdFZSdSdHdIZTG dJdK dKZUG dLdM dMeUZVdTdNdOZWdS )U    N)Iterablemd5)TYPE_CHECKINGAnyUnion)warn)anyascii)apps)settings)	LANG_INFO)cache)make_template_fragment_key)ImproperlyConfiguredSuspiciousOperation)setting_changed)Model)	ModelBase)receiver)HttpRequest)RequestFactory)	force_str)capfirstslugify)check_for_languageget_supported_language_variant)gettext_lazy)RemovedInWagtail70Warning)SiteWAGTAIL_APPEND_SLASHTc                 C   s   t dd|  dS )Nz&(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))z_\1_)resublowerstrip)str r&   P/var/www/lab.imftr.de/x/nb_venv/lib/python3.9/site-packages/wagtail/coreutils.pycamelcase_to_underscore'   s    r(   c                 C   s   t t| S )z$
    Convert a string to ascii.
    )r%   r	   valuer&   r&   r'   string_to_ascii.   s    r+   c                 C   s   | j jd | j S )z
    Returns a string that can be used to identify the specified model.

    The format is: `app_label.ModelName`

    This an be reversed with the `resolve_model_string` function
    .)_meta	app_label__name__modelr&   r&   r'   get_model_string6   s    r2   c                 C   s   t | tr^z| d\}}W n4 tyP   |dur<|}| }ntd| | Y n0 t||S t | trl| S td| d| dS )a  
    Resolve an 'app_label.model_name' string into an actual model class.
    If a model class is passed in, just return that.

    Raises a LookupError if a model can not be found, or ValueError if passed
    something that is neither a model or a string.
    r,   NzYCan not resolve {!r} into a model. Model names should be in the form app_label.model_namezCan not resolve z into a model)
isinstancer%   split
ValueErrorformatr
   Z	get_modeltype)Zmodel_stringZdefault_appr.   Z
model_namer&   r&   r'   resolve_model_stringA   s"    


r8   z<(-*)/script>c                 C   s   t dtd td| S )z
    Escape `</script>` tags in 'text' so that it can be placed within a `<script>` block without
    accidentally closing it. A '-' character will be inserted for each time it is escaped:
    `<-/script>`, `<--/script>` etc.
    zIThe `escape_script` hook is deprecated - use `template` elements instead.)categoryz<-\1/script>)r   r   	SCRIPT_REr"   )textr&   r&   r'   escape_scriptd   s
    r<   z[^\w\s-]c                 C   s:   t | } td| } td| } | ddd} t| S )a  
    Convert a string to ASCII exactly as Django's slugify does, with the exception
    that any non-ASCII alphanumeric characters (that cannot be ASCIIfied under Unicode
    normalisation) are escaped into codes like 'u0421' instead of being deleted entirely.

    This ensures that the result of slugifying (for example - Cyrillic) text will not be an empty
    string, and can thus be safely used as an identifier (albeit not a human-readable one).
    ZNFKD asciibackslashreplace)r   unicodedata	normalize
SLUGIFY_REr"   encodedecoder   r)   r&   r&   r'   cautious_slugifyt   s
    	rE   c                 C   s   t | }|dd}|S )a#  
    Convert a string to ASCII similar to Django's slugify, with cautious handling of
    non-ASCII alphanumeric characters. See `cautious_slugify`.

    Any inner whitespace, hyphens or dashes will be converted to underscores and
    will be safe for Django template or filename usage.
    -r    )rE   replace)r*   Zslugified_ascii_stringZsnake_case_stringr&   r&   r'   safe_snake_case   s    	rH   c                 C   s:   | du rt dS |  }|r,tt|jjS t| jS dS )z
    Return a human-readable label for a content type object, suitable for display in the admin
    in place of the default 'wagtailcore | page' representation
    NzUnknown content type)r    Zmodel_classr%   r   r-   Zverbose_namer1   )content_typer1   r&   r&   r'   get_content_type_label   s    rJ   c                 C   s>   t | }z|jf i |di W dS  ty8   Y dS 0 dS )zi
    Determine whether the callable `func` has a signature that accepts the keyword argument `kwarg`
    NTF)inspect	signaturebind_partial	TypeError)funckwargrL   r&   r&   r'   accepts_kwarg   s    
rQ   c                   @   s4   e Zd ZdZdZdd Zdd Zdd Zd	d
 ZdS )InvokeViaAttributeShortcuta  
    Used to create a shortcut that allows an object's named
    single-argument method to be invoked using a simple
    attribute reference syntax. For example, adding the
    following to an object:

    obj.page_url = InvokeViaAttributeShortcut(obj, 'get_page_url')

    Would allow you to invoke get_page_url() like so:

    obj.page_url.terms_and_conditions

    As well as the usual:

    obj.get_page_url('terms_and_conditions')
    objmethod_namec                 C   s   || _ || _d S NrS   )selfrT   rU   r&   r&   r'   __init__   s    z#InvokeViaAttributeShortcut.__init__c                 C   s   t | j| j}||S rV   )getattrrT   rU   )rW   namemethodr&   r&   r'   __getattr__   s    z&InvokeViaAttributeShortcut.__getattr__c                 C   s   | j | jdS )NrS   rS   rW   r&   r&   r'   __getstate__   s    z'InvokeViaAttributeShortcut.__getstate__c                 C   s   |d | _ |d | _d S )NrT   rU   rS   )rW   stater&   r&   r'   __setstate__   s    
z'InvokeViaAttributeShortcut.__setstate__N)	r/   
__module____qualname____doc__	__slots__rX   r\   r^   r`   r&   r&   r&   r'   rR      s   rR   c                 C   s`   |   j|d}|r |j|d}t|jddd}|}d}||v r\|d t| }|d7 }q:|S )a  
    Finds an available slug within the specified parent.

    If the requested slug is not available, this adds a number on the end, for example:

     - 'requested-slug'
     - 'requested-slug-1'
     - 'requested-slug-2'

    And so on, until an available slug is found.

    The `ignore_page_id` keyword argument is useful for when you are updating a page,
    you can pass the page being updated here so the page's current slug is not
    treated as in use by another page.
    )Zslug__startswith)idslugT)Zflat   rF   )Zget_childrenfilterexcludesetZvalues_listr%   )parentZrequested_slugZignore_page_idZpagesZexisting_slugsrf   numberr&   r&   r'   find_available_slug   s    
rm   c                  C   s   t tdd} ttj}| du rttj}z|| }W nP ty   |dd }z|| }W n" ty~   tj}tj||< Y n0 Y n0 ||fg} | D ]\}}||vrtd	|qt| S )z^
    Cache of settings.WAGTAIL_CONTENT_LANGUAGES in a dictionary for easy lookups by key.
    WAGTAIL_CONTENT_LANGUAGESNrF   r   zThe language {} is specified in WAGTAIL_CONTENT_LANGUAGES but not LANGUAGES. WAGTAIL_CONTENT_LANGUAGES must be a subset of LANGUAGES.)
rY   r   dict	LANGUAGESr   LANGUAGE_CODEKeyErrorr4   r   r6   )Zcontent_languages	languagesZdefault_language_codeZlanguage_nameZlanguage_coderZ   r&   r&   r'   get_content_languages  s.    

rt   i  )maxsizeFc                 C   s   | r| g}z| t|  d  W n ty2   Y n0 | dd }|| t }|D ]}||v rVt|rV|  S qV|s|D ]}||d r||  S q|t| dS )a  
    Return the language code that's listed in supported languages, possibly
    selecting a more generic variant. Raise LookupError if nothing is found.
    If `strict` is False (the default), look for a country-specific variant
    when neither the language code nor its generic variant is found.
    lru_cache should have a maxsize to prevent from memory exhaustion attacks,
    as the provided language codes are taken from the HTTP request. See also
    <https://www.djangoproject.com/weblog/2007/oct/26/security-fix/>.

    This is equvilant to Django's `django.utils.translation.get_supported_content_language_variant`
    but reads the `WAGTAIL_CONTENT_LANGUAGES` setting instead.
    fallbackrF   r   N)	extendr   rr   r4   appendrt   r   
startswithLookupError)Z	lang_codestrictZpossible_lang_codesZgeneric_lang_codeZsupported_lang_codescodeZsupported_coder&   r&   r'   &get_supported_content_language_variant/  s"    


r}   )returnc                  C   sB   ddl m}  td}|du r>dd | j D }td| |S )z?
    Cache of the locale id -> locale display name mapping
    r   )LocaleZwagtail_locales_display_nameNc                 S   s   i | ]}|j | qS r&   )pkZget_display_name).0localer&   r&   r'   
<dictcomp>\  s   z-get_locales_display_names.<locals>.<dictcomp>)wagtail.modelsr   r   getobjectsallrj   )r   Z
cached_mapr&   r&   r'   get_locales_display_namesS  s    
r   c                  K   s    | d dv rt   t  dS )zh
    Clear cache when global WAGTAIL_CONTENT_LANGUAGES/LANGUAGES/LANGUAGE_CODE settings are changed
    Zsetting)rn   rp   rq   N)rt   cache_clearr}   )kwargsr&   r&   r'   reset_cached  s    r   c                 C   s   | }| dD ]}z|| }W n tttttfy   zt||}W nd ttfy   |t|v rd z|t| }W n. ttttfy   td| d|Y n0 Y n0 Y n0 t	|rt|ddrt
d|d| }q|S )a  
    Like getattr, but accepts a dotted path as the accessor to be followed to any depth.
    At each step, the lookup on the object can be a dictionary lookup (foo['bar']) or an attribute
    lookup (foo.bar), and if it results in a callable, will be called (provided we can do so with
    no arguments, and it does not have an 'alters_data' property).

    Modelled on the variable resolution logic in Django templates:
    https://github.com/django/django/blob/f331eba6d576752dd79c4b37c41d981daa537fe6/django/template/base.py#L838
    r,   zFailed lookup for key [z] in Zalters_dataFzCannot call z from multigetattr)r4   rN   AttributeErrorrr   r5   
IndexErrorrY   dirintcallabler   )itemaccessorcurrentbitr&   r&   r'   multigetattrn  s4    
r   /)pathsiter   )r   r   r~   c                 C   s@   d}|r|j }|j}ntjd }|dkr,d}t|dj| |dS )a=  
    Return a simple ``HttpRequest`` instance that can be passed to
    ``Page.get_url()`` and other methods to benefit from improved performance
    when no real ``HttpRequest`` instance is available.

    If ``site`` is provided, the ``HttpRequest`` is made to look like it came
    from that Wagtail ``Site``.
    P   r   *zexample.com)ZSERVER_NAME)ZSERVER_PORT)hostnameportr   ZALLOWED_HOSTSr   r   )r   r   Zserver_portZserver_namer&   r&   r'   get_dummy_request  s    	
r       c                 C   s   t | |dS )a  
    Safely use the MD5 hash algorithm with the given ``data`` and a flag
    indicating if the purpose of the digest is for security or not.

    On security-restricted systems (such as FIPS systems), insecure hashes
    like MD5 are disabled by default. But passing ``usedforsecurity`` as
    ``False`` tells the underlying security implementation we're not trying
    to use the digest for secure purposes and to please just go ahead and
    allow it to happen.
    )usedforsecurityr   )datar   r&   r&   r'   safe_md5  s    r   c                   @   sj   e Zd ZdZedddZdd Zeddd	d
Ze	e ddddZ
dd Zdd Zdd Zdd ZdS )BatchProcessora  
    A class to help with processing of an unknown (and potentially very
    high) number of objects.

    Just set ``max_size`` to the maximum number of instances you want
    to be held in memory at any one time, and batches will be sent to the
    ``process()`` method as that number is reached, without you having to
    invoke ``process()`` regularly yourself. Just remember to invoke
    ``process()`` when you're done adding items, otherwise the final batch
    of objects will not be processed.
    )max_sizec                 C   s   || _ g | _d| _d S Nr   )r   itemsadded_count)rW   r   r&   r&   r'   rX     s    zBatchProcessor.__init__c                 C   s   | j S rV   )r   r]   r&   r&   r'   __len__  s    zBatchProcessor.__len__N)r   r~   c                 C   s<   | j | |  jd7  _| jr8t| j | jkr8|   d S Nrg   )r   rx   r   r   lenprocess)rW   r   r&   r&   r'   add  s    zBatchProcessor.additerabler~   c                 C   s   |D ]}|  | qd S rV   )r   )rW   r   r   r&   r&   r'   rw     s    zBatchProcessor.extendc                 C   s&   |    |   |   | j  d S rV   )pre_process_do_processingpost_processr   clearr]   r&   r&   r'   r     s    zBatchProcessor.processc                 C   s   dS )z
        A hook to allow subclasses to do any pre-processing of the data
        before the ``process()`` method is called.
        Nr&   r]   r&   r&   r'   r     s    zBatchProcessor.pre_processc                 C   s   t dS )z
        To be overridden by subclasses to do whatever it is
        that needs to be done to the items in ``self.items``.
        N)NotImplementedErrorr]   r&   r&   r'   r     s    zBatchProcessor._do_processingc                 C   s   dS )z
        A hook to allow subclasses to do any post-processing
        after the ``process()`` method is called, and before
        ``self.items`` is cleared
        Nr&   r]   r&   r&   r'   r     s    zBatchProcessor.post_process)r/   ra   rb   rc   r   rX   r   r   r   r   rw   r   r   r   r   r&   r&   r&   r'   r     s   r   c                       s   e Zd ZU dZdZeed< dddeed fddZd	d
 Z	dde
ddddZeee
eeef f  ddddZdd Zdd Z  ZS )BatchCreatora  
    A class to help with bulk creation of an unknown (and potentially very
    high) number of model instances.

    Just set ``max_size`` to the maximum number of instances you want
    to be held in memory at any one time, and batches of objects will
    be created as that number is reached, without you having to invoke
    the ``process()`` method regularly yourself. Just remember to
    invoke ``process()`` when you're done adding items, to ensure
    that the final batch items is saved.

    ``BatchSaver`` is migration-friendly! Just use the ``model``
    keyword argument when initializing to override the hardcoded model
    class with the version from your migration.
    Nr1   F)r1   ignore_conflicts)r   r1   c                   s*   t  | || _d| _|d ur&|| _d S r   )superrX   r   created_countr1   )rW   r   r1   r   	__class__r&   r'   rX     s
    zBatchCreator.__init__c                 C   s   | j f i |S rV   r0   )rW   r   r&   r&   r'   initialize_instance  s    z BatchCreator.initialize_instanceinstance)r   r~   c                K   sN   |d u r|  |}| j| |  jd7  _| jrJt| j| jkrJ|   d S r   )r   r   rx   r   r   r   r   )rW   r   r   r&   r&   r'   r     s    
zBatchCreator.addr   c                 C   s8   |D ].}t || jr"| j|d q| jf i | qd S )Nr   )r3   r1   r   )rW   r   r*   r&   r&   r'   rw   %  s    zBatchCreator.extendc                 C   s2   | j s
dS |  jt| jjj| j | jd7  _dS )z;
        Use bulk_create() to save ``self.items``.
        N)r   )r   r   r   r1   r   Zbulk_creater   r]   r&   r&   r'   r   ,  s    zBatchCreator._do_processingc                 C   s$   | j j}| j d| j d|j dS )Nr    z were created successfully.)r1   r-   r   r   Zverbose_name_plural)rW   optsr&   r&   r'   get_summary8  s    zBatchCreator.get_summary)r/   ra   rb   rc   r1   r   __annotations__r   rX   r   r   r   r   r   ro   r%   r   rw   r   r   __classcell__r&   r&   r   r'   r     s   
	$r   c                 C   s(   |du rg }| |j|jg t| |S )z
    A modified version of `make_template_fragment_key` which varies on page and
    site for use with `{% wagtailpagecache %}`.
    N)rw   	cache_keyre   r   )Zfragment_namepager   Zvary_onr&   r&   r'   "make_wagtail_template_fragment_key=  s    r   )N)N)F)r   T)N)X	functoolsrK   loggingr!   r@   collections.abcr   hashlibr   typingr   r   r   warningsr   r	   Zdjango.appsr
   Zdjango.confr   Zdjango.conf.localer   Zdjango.core.cacher   Zdjango.core.cache.utilsr   Zdjango.core.exceptionsr   r   Zdjango.core.signalsr   Zdjango.db.modelsr   Zdjango.db.models.baser   Zdjango.dispatchr   Zdjango.httpr   Zdjango.testr   Zdjango.utils.encodingr   Zdjango.utils.textr   r   Zdjango.utils.translationr   r   r   r    Zwagtail.utils.deprecationr   r   r   	getLoggerr/   loggerrY   r   r(   r+   r2   r8   compiler:   r<   UNICODErB   rE   rH   rJ   rQ   rR   rm   rt   	lru_cacher}   ro   r   r   r   r%   r   r   r   r   r   r&   r&   r&   r'   <module>   sp   

 
 $
 
*
#
	/
<?