a
    Dg!                  	   @  s  d dl mZ d dlZd dlZ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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Zd d	lm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+m,Z,m-Z- d dl.m/Z/ d dl0m1Z1 d dl2m3Z3 d dl4m5Z5 d dl6m7Z7m8Z8 d dl9m:Z: d dl;m<Z< d dl=m>Z? d dl@mAZA d dlBmCZC d dlDmEZE d dlFmGZGmHZH d dlImJZJmKZKmLZLmMZM d dlNmOZO d dlPmQZQmRZR d d lSmTZT d d!lUmVZV d d"lWmXZX eYd#ZZd$d%d&d'd(d)d*d+d,Z[G d-d. d.e\Z]G d/d0 d0eVe/j^Z_d1d2 Z`d3d4 Zad5d6 ZbG d7d8 d8ZcG d9d: d:e/jdjejfZgG d;d< d<e/jhZiG d=d> d>eceQeTjje/jkZlG d?d@ d@elZmG dAdB dBZnG dCdD dDZoedEdFdGgZpG dHdI dIeoZqG dJdK dKece/jkZrG dLdM dMerZsdS )N    )annotationsN)OrderedDictdefaultdict
namedtuple)Iterable)contextmanager)BytesIO)SpooledTemporaryFile)Any)apps)settings)checks)DEFAULT_CACHE_ALIASInvalidCacheBackendErrorcaches)	BaseCache)ImproperlyConfigured)File)InvalidStorageErrordefault_storagestorages)models)Q)flatatt)reverse)cached_propertyclassproperty)import_string)	mark_safe)gettext_lazy)TaggableManager)hooks)string_to_ascii)InvalidFilterSpecErrorUnknownOutputImageFormatError)FilterOperationFormatOperationImageTransformTransformOperation)Rect)CollectionMemberReferenceIndex)index)SearchableQuerySetMixin)hash_filelikezwagtail.imagesz.avifz.jpgz.pngz.gifz.webp.svgz.icoz.heic)avifjpegpnggifwebpsvgicoheicc                   @  s   e Zd ZdZdS )SourceImageIOErrorzb
    Custom exception to distinguish IOErrors that were thrown while opening the source image
    N)__name__
__module____qualname____doc__ r=   r=   T/var/www/lab.imftr.de/x/nb_venv/lib/python3.9/site-packages/wagtail/images/models.pyr8   E   s   r8   c                   @  s   e Zd Zdd ZdS )ImageQuerySetc                 G  sH   | j  }|j }|r2dd |D }|j|d}| tjd|ddS )z
        Prefetches generated renditions for the given filters.
        Returns all renditions when no filters are provided.
        c                 S  s    g | ]}t |tr|jn|qS r=   )
isinstanceFilterspec.0filterr=   r=   r>   
<listcomp>X   s   z5ImageQuerySet.prefetch_renditions.<locals>.<listcomp>)Zfilter_spec__in
renditionsprefetched_renditions)querysetZto_attr)modelget_rendition_modelobjectsallrE   Zprefetch_relatedr   ZPrefetch)selffiltersZrendition_modelrI   Zfilter_specsr=   r=   r>   prefetch_renditionsN   s    

z!ImageQuerySet.prefetch_renditionsN)r9   r:   r;   rP   r=   r=   r=   r>   r?   M   s   r?   c                 C  s
   |  |S )a  
    Obtain a valid upload path for an image file.

    This needs to be a module-level function so that it can be referenced within migrations,
    but simply delegates to the `get_upload_to` method of the instance, so that AbstractImage
    subclasses can override it.
    get_upload_toinstancefilenamer=   r=   r>   rR   g   s    rR   c                 C  s
   |  |S )a!  
    Obtain a valid upload path for an image rendition file.

    This needs to be a module-level function so that it can be referenced within migrations,
    but simply delegates to the `get_upload_to` method of the instance, so that AbstractRendition
    subclasses can override it.
    rQ   rS   r=   r=   r>   get_rendition_upload_tor   s    rV   c                  C  sh   t tdt} t| trdzt|  } W n@ tyb   zt| }| } W n ty\   t	dY n0 Y n0 | S )z
    Obtain the storage object for an image rendition file.
    Returns custom storage (if defined), or the default storage.

    This needs to be a module-level function, because we do not yet
    have an instance when Django loads the models.
    ZWAGTAILIMAGES_RENDITION_STORAGEz[WAGTAILIMAGES_RENDITION_STORAGE must be either a valid storage alias or dotted module path.)
getattrr   r   r@   strr   r   r   ImportErrorr   )storagemoduler=   r=   r>   get_rendition_storage}   s    

r\   c                   @  s4   e Zd Zdd Zdd Zedd Zedd Zd	S )
ImageFileMixinc                 C  s(   z| j j W dS  ty"   Y dS 0 dS )zM
        Returns True if the image is hosted on the local filesystem
        TFN)filepathNotImplementedErrorrN   r=   r=   r>   is_stored_locally   s
    z ImageFileMixin.is_stored_locallyc              
   C  s^   | j d u rXz| jj| _ W n0 tyH } ztt|W Y d }~n
d }~0 0 | jdgd | j S )N	file_sizeZupdate_fields)rc   r^   size	Exceptionr8   rX   save)rN   er=   r=   r>   get_file_size   s    
"zImageFileMixin.get_file_sizec              
   c  s   d}zJ| j }| j jrL|  r*| j d n| jdj}|| j jd}d}W n0 ty~ } zt	t
|W Y d }~n
d }~0 0 |d z|V  W |r|  n|r|  0 d S )NFrbr^   Tr   )r^   closedrb   open_meta	get_fieldrZ   nameOSErrorr8   rX   seekclose)rN   Z
close_file
image_filerZ   rh   r=   r=   r>   	open_file   s$    "

zImageFileMixin.open_filec                 c  s:   |   }tj|V  W d    n1 s,0    Y  d S N)rt   willowImagerl   )rN   rs   r=   r=   r>   get_willow_image   s    
zImageFileMixin.get_willow_imageN)r9   r:   r;   rb   ri   r   rt   rx   r=   r=   r=   r>   r]      s   
r]   c                   @  s    e Zd ZdZdd Zdd ZdS )WagtailImageFieldFilezS
    Override the ImageFieldFile in order to use Willow instead
    of Pillow.
    c                 C  s   t | ds|  | _| jS )zV
        override _get_image_dimensions to call our own get_image_dimensions.
        _dimensions_cache)hasattrget_image_dimensionsrz   ra   r=   r=   r>   _get_image_dimensions   s    

z+WagtailImageFieldFile._get_image_dimensionsc              	   C  sh   | j }zB|   tj| }| W |r2|   S | d S | d n|rX|   n
| d 0 dS )z
        The upstream ImageFieldFile calls a local function get_image_dimensions. In this implementation we've made get_image_dimensions
        a method to make it easier to override for Wagtail developers in the future.
        r   N)rk   rl   rv   rw   get_sizerr   rq   )rN   rr   imager=   r=   r>   r|      s    

  
z*WagtailImageFieldFile.get_image_dimensionsN)r9   r:   r;   r<   r}   r|   r=   r=   r=   r>   ry      s   ry   c                   @  s   e Zd ZdZeZdS )WagtailImageFieldzv
    Override the attr_class on the Django ImageField Model to inject our ImageFieldFile
    with Willow support.
    N)r9   r:   r;   r<   ry   Z
attr_classr=   r=   r=   r>   r      s   r   c                      s  e Zd ZejdeddZeededddZ	ejdded	d
dZ
ejedddZejedddZejeddddZejejeddddejdZde_eddeddZejdddZejdddZejdddZejdddZejdddZejdddddZe  Z! fddZ"dd Z#dd Z$dd  Z%d!d" Zd#d$ Z&e'd%d& Z(e)j*e+j,dd'd(e+-de+.de+/de+j,d)d'd(e+-d)ge+.d*e+.d+e+.d,g Z*d-d. Z0d/d0 Z1d1d2 Z2d3d4 Z3d5d6 Z4d7d8 Z5d9d: Z6d;d< Z7e8d=d> Z9d?d@dAdBZ:dCdDdEdFdGZ;dHdCdIdJdKZ<dLdCdIdMdNZ=dLdCdIdOdPZ>dHdQdRdSdTZ?dLdUdRdVdWZ@dLdUdRdXdYZAdLdZdCd[d\d]ZBdd^dLd_d_d[d`daZCdbdc ZDddde ZEdfdg ZFe'dhdi ZGe'djdk ZHdldm ZIG dndo doZJ  ZKS )pAbstractImage   title)
max_lengthverbose_namer^   widthheight)r   	upload_towidth_fieldheight_fieldTdescription )blankr   r   defaultF)r   editablez
created at)r   Zauto_now_adddb_indexzuploaded by user)r   nullr   r   	on_deleteNtags)	help_textr   r   )r   r   )r   r   (   )r   r   r   r   c                   s"   t  j|i | d| _d | _d S NF)super__init__
decorativecontextual_alt_text)rN   argskwargs	__class__r=   r>   r   .  s    zAbstractImage.__init__c                 C  s6   |   }t|| _W d    n1 s(0    Y  d S ru   )rt   r.   	file_hash)rN   fr=   r=   r>   _set_file_hash3  s    
zAbstractImage._set_file_hashc                 C  s&   | j dkr |   | jdgd | j S )Nr   r   rd   )r   r   rg   ra   r=   r=   r>   get_file_hash7  s    
zAbstractImage.get_file_hashc                 C  s,   | j   | j j| _|   | j d d S Nr   )r^   rl   re   rc   r   rq   ra   r=   r=   r>   _set_image_file_metadata>  s    

z&AbstractImage._set_image_file_metadatac                 C  s   d}| j jj|}ddd t|D }tj||}t|dkrt|d }tj	|\}}|d|  | }tj||}|S )z
        Generates a file path in the "original_images" folder.
        Ensuring ASCII characters and limiting length to prevent filesystem issues during uploads.
        Zoriginal_imagesr   c                 s  s"   | ]}t |d k r|ndV  qdS )   _N)ord)rD   ir=   r=   r>   	<genexpr>S  s   z.AbstractImage.get_upload_to.<locals>.<genexpr>_   ^   N)
r^   fieldrZ   get_valid_namejoinr"   osr_   lensplitext)rN   rU   folder_name	full_pathZchars_to_trimprefix	extensionr=   r=   r>   rR   H  s    
zAbstractImage.get_upload_toc                 C  s
   t | S ru   )r+   Zget_grouped_references_tora   r=   r=   r>   	get_usageb  s    zAbstractImage.get_usagec                 C  s   t d| jfdS )Nzwagtailimages:image_usage)r   )r   idra   r=   r=   r>   	usage_urle  s    zAbstractImage.usage_url
   )Zboostro   uploaded_by_user
created_atr   c                 C  s   | j S ru   )r   ra   r=   r=   r>   __str__y  s    zAbstractImage.__str__c                 C  s\   t |tjstS | jj|jjkr$dS | j}|du r:| |u S ||jkoZ|j| jkoZ|j| jkS )a  
        Customise the definition of equality so that two Image instances referring to the same
        image but different contextual alt text or decorative status are considered different.
        All other aspects are copied from Django's base `Model` implementation.
        FN)	r@   r   ModelNotImplementedrm   Zconcrete_modelpkr   r   )rN   otherZmy_pkr=   r=   r>   __eq__|  s    


zAbstractImage.__eq__c                 C  s&   | j du rtdt| j | j| jfS )zH
        Match the semantics of the custom equality definition.
        Nz8Model instances without primary key value are unhashable)r   	TypeErrorhashr   r   ra   r=   r=   r>   __hash__  s    
zAbstractImage.__hash__c                 C  s   t dd| j| jS r   )r)   r   r   ra   r=   r=   r>   get_rect  s    zAbstractImage.get_rectc                 C  sD   | j d ur@| jd ur@| jd ur@| jd ur@t| j | j| j| jS d S ru   )focal_point_xfocal_point_yfocal_point_widthfocal_point_heightr)   
from_pointra   r=   r=   r>   get_focal_point  s    zAbstractImage.get_focal_pointc                 C  s   |   d uS ru   )r   ra   r=   r=   r>   has_focal_point  s    zAbstractImage.has_focal_pointc                 C  sF   |d ur*|j | _|j| _|j| _|j| _nd | _d | _d | _d | _d S ru   )Z
centroid_xr   Z
centroid_yr   r   r   r   r   )rN   Zrectr=   r=   r>   set_focal_point  s    
zAbstractImage.set_focal_pointc                 C  sV  |   rd S |  }| }|rztdd |D }tdd |D }tdd |D }tdd |D }t||||}nt| }|rtdd |D }tdd |D }tdd |D }td	d |D }t||||}nW d    d S W d    n1 s0    Y  |j\}	}
|j\}}|d
9 }|d
9 }t|d}t|d}t	|	|
||S )Nc                 s  s   | ]}|d  V  qdS r   Nr=   rD   facer=   r=   r>   r         z:AbstractImage.get_suggested_focal_point.<locals>.<genexpr>c                 s  s   | ]}|d  V  qdS    Nr=   r   r=   r=   r>   r     r   c                 s  s   | ]}|d  V  qdS )   Nr=   r   r=   r=   r>   r     r   c                 s  s   | ]}|d  V  qdS )   Nr=   r   r=   r=   r>   r     r   c                 s  s   | ]}|d  V  qdS r   r=   rD   featurer=   r=   r>   r     r   c                 s  s   | ]}|d  V  qdS r   r=   r   r=   r=   r>   r     r   c                 s  s   | ]}|d  V  qdS r   r=   r   r=   r=   r>   r     r   c                 s  s   | ]}|d  V  qdS r   r=   r   r=   r=   r>   r     r   g333333?d   )
is_svgrx   Zdetect_facesminmaxr)   Zdetect_featuresZcentroidre   r   )rN   rv   ZfaceslefttoprightZbottomfocal_pointfeaturesxyr   r   r=   r=   r>   get_suggested_focal_point  s2    
0



z'AbstractImage.get_suggested_focal_pointc                 C  s
   | j jjS )z,Get the Rendition model for this Image model)rG   relZrelated_modelclsr=   r=   r>   rK     s    z!AbstractImage.get_rendition_modelz"Iterable[AbstractRendition] | Nonereturnc                 C  s&   dt | di v r| j S t | dd S )NrG   _prefetched_objects_cacherH   )rW   rG   rM   ra   r=   r=   r>   _get_prefetched_renditions  s    
z(AbstractImage._get_prefetched_renditionsAbstractRenditionNone)	renditionr   c              	   C  sV   z| j d j| W n ttfy,   Y n0 z| j| W n tyP   Y n0 d S NrG   )r   Z_result_cacheappendAttributeErrorKeyErrorrH   )rN   r   r=   r=   r>   _add_to_prefetched_renditions  s    z+AbstractImage._add_to_prefetched_renditionszFilter | str)rE   r   c                 C  s|   |   }t|trt|d}z| |}W n( |jyR   | |}| | Y n0 || |	| |j
}|j|| |S )a,  
        Returns a ``Rendition`` instance with a ``file`` field value (an
        image) reflecting the supplied ``filter`` value and focal point values
        from this object.

        Note: If using custom image models, an instance of the custom rendition
        model will be returned.
        rB   )rK   r@   rX   rA   find_existing_renditionDoesNotExistcreate_renditionr   construct_cache_keyget_cache_keyrB   cache_backendset)rN   rE   	Renditionr   	cache_keyr=   r=   r>   get_rendition  s    	


zAbstractImage.get_renditionrA   c                 C  s6   |   }z| || W S  ty0   |jY n0 dS )a  
        Returns an existing ``Rendition`` instance with a ``file`` field value
        (an image) reflecting the supplied ``filter`` value and focal point
        values from this object.

        If no such rendition exists, a ``DoesNotExist`` error is raised for the
        relevant model.

        Note: If using custom image models, an instance of the custom rendition
        model will be returned.
        N)rK   find_existing_renditionsr   r   )rN   rE   r   r=   r=   r>   r     s
    z%AbstractImage.find_existing_renditionc                 C  s,   | j j|j|| d| |id\}}|S )a  
        Creates and returns a ``Rendition`` instance with a ``file`` field
        value (an image) reflecting the supplied ``filter`` value and focal
        point values from this object.

        This method is usually called by ``Image.get_rendition()``, after first
        checking that a suitable rendition does not already exist.

        Note: If using custom image models, an instance of the custom rendition
        model will be returned.
        r^   )filter_specfocal_point_keydefaults)rG   Zget_or_createrB   r   generate_rendition_file)rN   rE   r   createdr=   r=   r>   r   &  s    
zAbstractImage.create_renditiondict[str, AbstractRendition])rO   r   c                   s      t|d tr.dd t| D }j| fdd|D }j|  D ]\}}	| ||< qX fdd D }|r j
| fdd|D S )a  
        Returns a ``dict`` of ``Rendition`` instances with image files reflecting
        the supplied ``filters``, keyed by filter spec patterns.

        Note: If using custom image models, instances of the custom rendition
        model will be returned.
        r   c                 S  s   g | ]}t |qS r=   )rA   )rD   rB   r=   r=   r>   rF   F  r   z0AbstractImage.get_renditions.<locals>.<listcomp>c                   s   g | ]}| vr|qS r=   r=   rD   r   rG   r=   r>   rF   L  r   c                   s4   i | ],\}}t |d ds ||j|qS )_from_cacheF)rW   r   r   rB   )rD   rE   r   r   rN   r=   r>   
<dictcomp>R  s   z0AbstractImage.get_renditions.<locals>.<dictcomp>c                   s   i | ]}|j  | qS r=   r   rC   r  r=   r>   r	  ^  r   )rK   r@   rX   dictfromkeyskeysr   create_renditionsitemsr   r   Zset_many)rN   rO   	not_foundrE   r   Zcache_additionsr=   )r   rG   rN   r>   get_renditions;  s    


zAbstractImage.get_renditionszdict[Filter, AbstractRendition]c              	     sf     dd |D }i  }|durtt}|D ]8}z||j }W n ty^   Y q6Y q60 || | q6| D ]8\}}|}|D ] }|j	|krd|_
||<  qxqqxn fdd| D }	 j|	 D ]}||j }|_||< q܇fdd|D }
|
rbt }|
D ]}|t|j|dO }qj|D ]}||j }||< qHS )	a  
        Returns a dictionary of existing ``Rendition`` instances with ``file``
        values (images) reflecting the supplied ``filters`` and the focal point
        values from this object.

        Filters for which an existing rendition cannot be found are omitted
        from the return value. If none of the requested renditions have been
        created before, the return value will be an empty dict.
        c                 S  s   i | ]}|j |qS r=   r   r  r=   r=   r>   r	  m  r   z:AbstractImage.find_existing_renditions.<locals>.<dictcomp>NTc                   s$   g | ]\}}  ||qS r=   )r   r   )rD   rB   rE   r  r=   r>   rF     s   z:AbstractImage.find_existing_renditions.<locals>.<listcomp>c                   s   g | ]}| vr|qS r=   r=   r  )foundr=   r>   rF     r   r   r   )rK   r   r   listr   r   r   r  r   r   r  r   Zget_manyvaluesr   r   rB   rG   rE   )rN   rO   Zfilters_by_specrH   Zpotential_matchesr   rE   rG   r   Z
cache_keysr  lookup_qr=   )r   r  rN   r>   r   `  sL    






z&AbstractImage.find_existing_renditionsc                   s    }|si S t|dkr2|d }||iS i }dd |D } }| W d   n1 sj0    Y  g }tjjddB tj fdd	|D D ]}|	|
  qW d   n1 s0    Y  g }	t }
|D ]}|
t|j|jd
O }
qj|
D ]Z}||j }|||< t|D ]8}|j|jkr*|j|jkr*|| |		|j q*q|jj|ddD ]}||j }|||< qx|	D ]}|jdd q|S )aP  
        Creates multiple ``Rendition`` instances with image files reflecting the supplied
        ``filters``, and returns them as a ``dict`` keyed by the relevant ``Filter`` instance.
        Where suitable renditions already exist in the database, they will be returned instead,
        so as not to create duplicates.

        This method is usually called by ``Image.get_renditions()``, after first
        checking that a suitable rendition does not already exist.

        Note: If using custom image models, an instance of the custom rendition
        model will be returned.
        r   r   c                 S  s   i | ]}|j |qS r=   r   r  r=   r=   r>   r	    r   z3AbstractImage.create_renditions.<locals>.<dictcomp>Nr   )max_workersc                 3  s"   | ]}  j|tV  qd S ru   )Zsubmitgenerate_rendition_instancer   rC   executorZoriginal_image_bytesrN   r=   r>   r     s   z2AbstractImage.create_renditions.<locals>.<genexpr>r  T)Zignore_conflictsF)rg   )rK   r   r   rt   read
concurrentZfuturesZThreadPoolExecutorZas_completedr   resultr   r   r   rG   rE   r  remover^   rL   Zbulk_createdelete)rN   rO   r   rE   Zreturn_valueZ
filter_mapr^   Z	to_createfutureZfiles_for_deletionr  r   existingnewr=   r  r>   r    sN    
&.





zAbstractImage.create_renditionsr   )rE   sourcer   c              
   C  s2   |   | |j|| | j|t|| jjdddS )z
        Use the supplied ``source`` image to create and return an
        **unsaved** ``Rendition`` instance, with a ``file`` value reflecting
        the supplied ``filter`` value and focal point values from this object.
        ro   r"  )r   r   r   r^   )rK   rB   r   r  r   r^   ro   )rN   rE   r"  r=   r=   r>   r    s    z)AbstractImage.generate_rendition_instancer$  r   c                C  s   | | }td|j| j t }z<|j| ttj	d|d}td|j| jt | d  W n    td|j| j  Y n0 t
j| jj}t
j|\}}|jddt|j  }	|r|d |	 }	|d	d
t|	  }
|
d |	 }t|j|dS )a  
        Generates an in-memory image matching the supplied ``filter`` value
        and focal point value from this object, wraps it in a ``File`` object
        with a suitable filename, and returns it. The return value is used
        as the ``file`` field value for rendition objects saved by
        ``AbstractImage.create_rendition()``.

        If the contents of ``self.file`` has already been read into memory, the
        ``source`` keyword can be used to provide a reference to the in-memory
        ``File``, bypassing the need to reload the image contents from storage.

        NOTE: The responsibility of generating the new image from the original
        falls to the supplied ``filter`` object. If you want to do anything
        custom with rendition images (for example, to preserve metadata from
        the original image), you might want to consider swapping out ``filter``
        for an instance of a custom ``Filter`` subclass of your design.
        z&Generating '%s' rendition for image %d)max_sizer$  z/Generated '%s' rendition for image %d in %.1fmsi  z.Failed to generate '%s' rendition for image %d|.N;   r#  )r   loggerdebugrB   r   timerunr	   r   ZFILE_UPLOAD_MAX_MEMORY_SIZEr   r_   basenamer^   ro   r   replaceIMAGE_FORMAT_EXTENSIONSformat_namer   r   r   )rN   rE   r"  r   
start_timeZgenerated_imageZinput_filenameZ input_filename_without_extensionZinput_extensionZoutput_extensionZ!output_filename_without_extensionZoutput_filenamer=   r=   r>   r    sR    

z%AbstractImage.generate_rendition_filec                 C  s   | j | jk S ru   )r   r   ra   r=   r=   r>   is_portraitV  s    zAbstractImage.is_portraitc                 C  s   | j | jk S ru   )r   r   ra   r=   r=   r>   is_landscapeY  s    zAbstractImage.is_landscapec                 C  s    t j| jj\}}| dkS )Nr/   )r   r_   r   r^   ro   lower)rN   r   extr=   r=   r>   r   \  s    zAbstractImage.is_svgc                 C  s   t j| jjS ru   )r   r_   r-  r^   ro   ra   r=   r=   r>   rU   `  s    zAbstractImage.filenamec                 C  s   t | dd p| jS )Nr   )rW   r   ra   r=   r=   r>   default_alt_textd  s    zAbstractImage.default_alt_textc                 C  s   ddl m} ||d| S )Nr   )permission_policyZchange)Zwagtail.images.permissionsr7  Z user_has_permission_for_instance)rN   userr7  r=   r=   r>   is_editable_by_userk  s    z!AbstractImage.is_editable_by_userc                   @  s   e Zd ZdZdS )zAbstractImage.MetaTNr9   r:   r;   abstractr=   r=   r=   r>   Metap  s   r<  )Lr9   r:   r;   r   	CharFieldr   r   r   rR   r^   r   IntegerFieldr   r   ZDateTimeFieldr   
ForeignKeyr   ZAUTH_USER_MODELZSET_NULLr   wagtail_reference_index_ignorer    r   ZPositiveIntegerFieldr   r   r   r   rc   r   r?   Z
as_managerrL   r   r   r   r   r   propertyr   r*   Zsearch_fieldsr,   ZSearchFieldZAutocompleteFieldZFilterFieldZRelatedFieldsr   r   r   r   r   r   r   r   classmethodrK   r   r   r   r   r   r  r   r  r  r  r2  r3  r   rU   r6  r9  r<  __classcell__r=   r=   r   r>   r     s   


(
%KTF

r   c                   @  s"   e Zd ZdZG dd dejZdS )rw   )	r   r^   r   Z
collectionr   r   r   r   r   c                   @  s"   e Zd ZedZedZdgZdS )z
Image.Metar   images)Zchoose_imagezCan choose imageN)r9   r:   r;   r   r   Zverbose_name_pluralZpermissionsr=   r=   r=   r>   r<    s   r<  N)r9   r:   r;   Zadmin_form_fieldsr   r<  r=   r=   r=   r>   rw   t  s   rw   c                   @  s   e Zd ZdZedZedZedZedZ	d!ddZ
ed	d
dddZedd Zedd Zedd Zd"ddZed#dddddZd$ddddddZdd  ZdS )%rA   z
    Represents one or more operations that can be applied to an Image to produce a rendition
    appropriate for final display on the website. Usually this would be a resize operation,
    but could potentially involve colour processing, etc.
    z^[A-Za-z0-9_\-\.]+$z^[A-Za-z0-9_\-\.\|]+$z^[A-Za-z0-9_\-\.{},]+$z^[A-Za-z0-9_\-\.{},\|]+$Nc                 C  s
   || _ d S ru   r   )rN   rB   r=   r=   r>   r     s    zFilter.__init__zstr | Iterable[str]z	list[str])rB   r   c           	        s   t |tr$d|v rdnd}||}g }|D ]b}d|v rd|v r|d\ }|d\}|d}| fdd|D  q,||g q,tj| }dd |D S )	z
        Converts a spec pattern with brace-expansions, into a list of spec patterns.
        For example, "width-{100,200}" becomes ["width-100", "width-200"].

        Supports providing filter specs already split, or pipe or space-separated.
        r&   {},c                   s   g | ]} |  qS r=   r=   )rD   optionr   suffixr=   r>   rF     r   z&Filter.expand_spec.<locals>.<listcomp>c                 S  s   g | ]}d  |qS )r&  )r   )rD   Zcombinationr=   r=   r>   rF     r   )r@   rX   splitr   	itertoolsproduct)	rN   rB   	separatorZexpanded_segmentssegmentZoptions_suffixedZoptions_patternoptionscombinationsr=   rJ  r>   expand_spec  s    



zFilter.expand_specc                 C  s~   i }t dD ]}|t|  qg }| jdD ]D}|d}|d |vr^td|d  ||d  }|||  q4|S )NZregister_image_operationsr&  -r   zUnrecognised operation: %s)r!   Z	get_hooksupdater
  rB   rL  r#   r   )rN   Zregistered_operationsfn
operationsZop_specZop_spec_partsZop_classr=   r=   r>   rW    s    

zFilter.operationsc                 C  s   dd | j D S )Nc                 S  s   g | ]}t |tr|qS r=   )r@   r(   rD   	operationr=   r=   r>   rF     s   
z/Filter.transform_operations.<locals>.<listcomp>rW  ra   r=   r=   r>   transform_operations  s    zFilter.transform_operationsc                 C  s   dd | j D S )Nc                 S  s   g | ]}t |tr|qS r=   )r@   r%   rX  r=   r=   r>   rF     s   
z,Filter.filter_operations.<locals>.<listcomp>rZ  ra   r=   r=   r>   filter_operations  s    zFilter.filter_operationsc                 C  s<   |s|j |jf}t|| d}| jD ]}|||}q&|S )ac  
        Returns an ImageTransform with all the transforms in this filter applied.

        The ImageTransform is an object with two attributes:
         - .size - The size of the final image
         - .matrix - An affine transformation matrix that combines any
           transform/scale/rotation operations that need to be applied to the image
        )Zimage_is_svg)r   r   r'   r   r[  r,  )rN   r   re   	transformrY  r=   r=   r>   get_transform  s    

zFilter.get_transformr   r   )r   r"  c                 c  sJ   |d urt j|V  n.| }|V  W d    n1 s<0    Y  d S ru   )rv   rw   rl   rx   )rN   r   r"  Zwillow_imager=   r=   r>   rx     s    
zFilter.get_willow_imager   )r   outputr"  c                 C  sV  |  ||2}|j}| }| ||jj|jjf}|| 	 }|
|j}d|i}| jD ]}||||pv|}qbd|v r|d }	n@ddddd}
| sd|
d< ttdi }|
| |
||}	|	dkr(d|v r|d }nttd	d
}| r|d}|j||dddW  d    S |	dkrN|j|ddW  d    S |	dkrp||W  d    S |	dkrd|v rd|d v r|j|ddW  d    S d|v r|d }nttdd}|j||dW  d    S |	dkrdd|v r(d|d v r(|j|ddW  d    S d|v r<|d }nttdd}|j||dW  d    S |	dkrd|v rd|d v r|j|ddW  d    S d|v r|d }nttdd}|j||dW  d    S |	dkr ||W  d    S |	dkr"||W  d    S td|	 d W d    n1 sH0    Y  d S )!Nzoriginal-formatzoutput-formatr2   r1   )r0   Zbmpr4   r7   r3   Z WAGTAILIMAGES_FORMAT_CONVERSIONSzjpeg-qualityZWAGTAILIMAGES_JPEG_QUALITYU   )r   r   r   T)qualityZprogressiveoptimize)rb  r4   zoutput-format-optionslossless)rc  zwebp-qualityZWAGTAILIMAGES_WEBP_QUALITYP   )ra  r0   zavif-qualityZWAGTAILIMAGES_AVIF_QUALITYr7   zheic-qualityZWAGTAILIMAGES_HEIC_QUALITYr5   r6   zUnknown output image format '')rx   r0  Zauto_orientr^  r   r   r   Zcropr   roundresizere   r\  r,  Zhas_animationrW   r   rU  getZ	has_alphaZset_background_color_rgbZsave_as_jpegZsave_as_pngZsave_as_gifZsave_as_webpZsave_as_avifZsave_as_heicZsave_as_svgZsave_as_icor$   )rN   r   r_  r"  rv   Zoriginal_formatr]  envrY  Zoutput_formatZdefault_conversions
conversionra  r=   r=   r>   r,    s    























z
Filter.runc                 C  sj   g }| j D ]0}t|dg D ]}t||d}|t| qq
d|}|sNdS t|d d d S )NZvary_fieldsr   rT  zutf-8   )	rW  rW   r   rX   r   hashlibsha1encode	hexdigest)rN   r   Z
vary_partsrY  r   valueZvary_stringr=   r=   r>   r   f  s    

zFilter.get_cache_key)N)N)N)N)r9   r:   r;   r<   recompileZspec_patternZpipe_spec_patternZexpanding_spec_patternZpipe_expanding_spec_patternr   rB  rS  r   rW  rA  r[  r\  r^  r   rx   r,  r   r=   r=   r=   r>   rA     s&   








prA   c                   @  sZ   e Zd ZdZddddddZedd	d
dZdd Zdd Zdd Z	d dddZ
dS )ResponsiveImagez
    A custom object used to represent a collection of renditions.
    Provides a 'renditions' property to access the renditions,
    and renders to the front-end HTML.
    Nr  dict[str, Any] | NonerG   attrsc                 C  s   t | | _|| _d S ru   )r  r  rG   rv  rN   rG   rv  r=   r=   r>   r   ~  s    zResponsiveImage.__init__zlist[AbstractRendition])renditions_listc                 C  s*   t |dkr|d jS ddd |D S )Nr   r   z, c                 S  s    g | ]}|j  d |j dqS )rE  w)urlr   )rD   rr=   r=   r>   rF     r   z4ResponsiveImage.get_width_srcset.<locals>.<listcomp>)r   rz  r   )r   rx  r=   r=   r>   get_width_srcset  s    
z ResponsiveImage.get_width_srcsetc                 C  s8   | j pi }t| jdkr(| | j|d< | jd |S )Nr   srcsetr   )rv  r   rG   r|  img_tag)rN   rv  r=   r=   r>   __html__  s    
zResponsiveImage.__html__c                 C  s   t |  S ru   )r   r  ra   r=   r=   r>   r     s    zResponsiveImage.__str__c                 C  s
   t | jS ru   )boolrG   ra   r=   r=   r>   __bool__  s    zResponsiveImage.__bool__)r   c                 C  s&   t |tr"| j|jko | j|jkS dS r   )r@   rs  rG   rv  )rN   r   r=   r=   r>   r     s    
zResponsiveImage.__eq__)N)r9   r:   r;   r<   r   rB  r|  r  r   r  r   r=   r=   r=   r>   rs  w  s   	 
rs  
FileFormatro   	mime_typec                      sv   e Zd Zeddeddeddedded	d
gZdddd fddZdddddZdd Z fddZ  Z	S )Picturer0   z
image/avifr4   z
image/webpr1   z
image/jpegr2   z	image/pngr3   z	image/gifNr  rt  ru  c                   s   t  || | || _d S ru   )r   r   get_formatsformatsrw  r   r=   r>   r     s    zPicture.__init__z"dict[str, list[AbstractRendition]])rG   r   c                 C  s^   t t}| D ]4\}}tjD ]$}d| |v r|| |  qqqt| dk rZi S |S )z
        Group renditions by the format they are for, if any.
        If there is only one format, no grouping is required.
        zformat-r   )r   r  r  r&   Zsupported_formatsr   r   r  )rN   rG   r  rB   r   fmtr=   r=   r>   r    s    
zPicture.get_formatsc                 C  s*   t | jD ]}|j| jv r
|j  S q
d S ru   )reversedsource_format_orderro   r  )rN   r  r=   r=   r>   get_fallback_format  s    zPicture.get_fallback_formatc           
   
     s   | j stdt   dS | jp$i }d|v r>d|d  dnd}|  }| j | }g }| jD ]P}|j|kr^|j| j v r^| | j |j }|j	}|
d| d| d| d	 q^t|d
kr| ||d< |d |}	tdd| |	 dS )Nz	<picture>z
</picture>sizeszsizes="z" r   z<source srcset="ztype="z">r   r}  r   )r  r   r   r  rv  r  r  ro   r|  r  r   r   r~  r   )
rN   rv  r  Zfallback_formatZfallback_renditionssourcesr  r}  mimefallbackr   r=   r>   r    s     


 zPicture.__html__)N)
r9   r:   r;   r  r  r   r  r  r  rC  r=   r=   r   r>   r    s    	r  c                      s$  e Zd ZejdddZeeedddZ	ej
ddZej
ddZejd	dd
ddZdZedd Zedd Zedd Zedd Zedd Zedd Zedd Zedd Zi fddZdd Zd d! Ze fd"d#Zed$d% Ze d&d'd(d)Z!d*d+ Z"d,d- Z#G d.d/ d/Z$  Z%S )0r   r   T)r   r   r   r   )r   rZ   r   r   F)r      r   )r   r   r   r   c                 C  s   | j jS ru   )r^   rz  ra   r=   r=   r>   rz    s    zAbstractRendition.urlc                 C  s@   t | jdr| jjrdS t | jdr4| jjr4| jjS | jjS d S )Nr   r   r   )r{   r   r   r   r6  ra   r=   r=   r>   alt  s    
zAbstractRendition.altc                 C  s
   t | jS )zg
        The src, width, height, and alt attributes for an <img> tag, as a HTML
        string
        )r   
attrs_dictra   r=   r=   r>   rv    s    zAbstractRendition.attrsc                 C  s(   t d| jfd| jfd| jfd| jfgS )zX
        A dict of the src, width, height, and alt attributes for an <img> tag.
        srcr   r   r  )r   rz  r   r   r  ra   r=   r=   r>   r    s    zAbstractRendition.attrs_dictc                 C  s(   | j }ttdr$|dr$tj| }|S )NWAGTAILADMIN_BASE_URL/)rz  r{   r   
startswithr  )rN   rz  r=   r=   r>   full_url+  s    
zAbstractRendition.full_urlc                 C  s
   t | jS ru   )rA   r   ra   r=   r=   r>   rE   2  s    zAbstractRendition.filterc                 C  s*   | j  }|r&| j| j }||S d S ru   )r   r   rE   r^  r]  )rN   Zimage_focal_pointr]  r=   r=   r>   r   6  s    
zAbstractRendition.focal_pointc                 C  sL   | j }|rDt|jd | j }t|jd | j }d| d| dS dS dS )a  
        Returns a `background-position` rule to be put in the inline style of an element which uses the rendition for its background.

        This positions the rendition according to the value of the focal point. This is helpful for when the element does not have
        the same aspect ratio as the rendition.

        For example:

            {% image page.image fill-1920x600 as image %}
            <div style="background-image: url('{{ image.url }}'); {{ image.background_position_style }}">
            </div>
        r   zbackground-position: z% z%;zbackground-position: 50% 50%;N)r   intr   r   r   r   )rN   r   ZhorzZvertr=   r=   r>   background_position_style=  s    z+AbstractRendition.background_position_stylec                 C  s:   | j  }|tdj || tdt| dS )NZwagtailimagesz<img>)r  copyrU  r   Zget_app_configZdefault_attrsr   r   )rN   Zextra_attributesrv  r=   r=   r>   r~  S  s    

zAbstractRendition.img_tagc                 C  s   |   S ru   )r~  ra   r=   r=   r>   r  \  s    zAbstractRendition.__html__c                 C  s"   d}| j jj|}tj||S )z{
        Generates a file path within the "images" folder by combining the folder name and the validated filename.
        rD  )r^   r   rZ   r   r   r_   r   )rN   rU   r   r=   r=   r>   rR   _  s    zAbstractRendition.get_upload_toc                   sP   t  jf i |}| jjsLtdd | jjD sL|tjd|  d| dd |S )Nc                 s  s   | ]}t |h d kV  qdS )>   r   r   r   N)r   )rD   
constraintr=   r=   r>   r   k  s   z*AbstractRendition.check.<locals>.<genexpr>z@Custom rendition model %r has an invalid unique_together settingzCustom rendition models must include the constraint ('image', 'filter_spec', 'focal_point_key') in their unique_together definition.zwagtailimages.E001)hintobjr   )	r   checkrm   r;  anyunique_togetherr   r   Error)r   r   errorsr   r=   r>   r  g  s     zAbstractRendition.checkc                 C  s   dd t| j| j||g S )Nzwagtail-rendition-rT  )r   rX   r   r   )r   Zfilter_cache_keyr   r=   r=   r>   r   |  s    z%AbstractRendition.construct_cache_keyr   r   c                 C  s*   z
t d W S  ty$   t t  Y S 0 d S r   )r   r   r   r   r=   r=   r>   r     s    
zAbstractRendition.cache_backendc                 C  s   |  | j| j| jS ru   )r   r   r   r   ra   r=   r=   r>   r     s    zAbstractRendition.get_cache_keyc                 C  s   | j |   d S ru   )r   r  r   ra   r=   r=   r>   purge_from_cache  s    z"AbstractRendition.purge_from_cachec                   @  s   e Zd ZdZdS )zAbstractRendition.MetaTNr:  r=   r=   r=   r>   r<    s   r<  )&r9   r:   r;   r   r=  r   r   rV   r\   r^   r>  r   r   r   r@  rA  rz  r  rv  r  r  rE   r   r   r  r~  r  rR   rB  r  staticmethodr   r   r   r   r  r<  rC  r=   r=   r   r>   r     sR   







	
r   c                   @  s,   e Zd ZejedejdZG dd dZdS )r   rG   )Zrelated_namer   c                   @  s   e Zd ZdZdS )zRendition.Meta))r   r   r   N)r9   r:   r;   r  r=   r=   r=   r>   r<    s   r<  N)	r9   r:   r;   r   r?  rw   ZCASCADEr   r<  r=   r=   r=   r>   r     s   r   )t
__future__r   concurrent.futuresr  rl  rM  loggingos.pathr   rq  r+  collectionsr   r   r   collections.abcr   
contextlibr   ior   tempfiler	   typingr
   rv   Zdjango.appsr   Zdjango.confr   Zdjango.corer   Zdjango.core.cacher   r   r   Zdjango.core.cache.backends.baser   Zdjango.core.exceptionsr   Zdjango.core.filesr   Zdjango.core.files.storager   r   r   Z	django.dbr   Zdjango.db.modelsr   Zdjango.forms.utilsr   Zdjango.urlsr   Zdjango.utils.functionalr   r   Zdjango.utils.module_loadingr   Zdjango.utils.safestringr   Zdjango.utils.translationr   r   Ztaggit.managersr    Zwagtailr!   Zwagtail.coreutilsr"   Zwagtail.images.exceptionsr#   r$   Zwagtail.images.image_operationsr%   r&   r'   r(   Zwagtail.images.rectr)   Zwagtail.modelsr*   r+   Zwagtail.searchr,   Zwagtail.search.querysetr-   Zwagtail.utils.filer.   	getLoggerr)  r/  IOErrorr8   ZQuerySetr?   rR   rV   r\   r]   fieldsfilesZImageFieldFilery   Z
ImageFieldr   ZIndexedr   r   rw   rA   rs  r  r  r   r   r=   r=   r=   r>   <module>   s   
B	    w o-L #