Initial commit
This commit is contained in:
0
env/lib/python3.10/site-packages/wagtail/utils/__init__.py
vendored
Normal file
0
env/lib/python3.10/site-packages/wagtail/utils/__init__.py
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/apps.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/apps.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/decorators.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/decorators.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/deprecation.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/deprecation.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/file.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/file.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/loading.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/loading.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/registry.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/registry.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/sendfile.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/sendfile.cpython-310.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/setup.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/setup.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/text.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/text.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/timestamps.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/timestamps.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/urlpatterns.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/urlpatterns.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/utils.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/utils.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/version.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/version.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/widgets.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/utils/__pycache__/widgets.cpython-310.pyc
vendored
Normal file
Binary file not shown.
23
env/lib/python3.10/site-packages/wagtail/utils/apps.py
vendored
Normal file
23
env/lib/python3.10/site-packages/wagtail/utils/apps.py
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
from importlib import import_module
|
||||
|
||||
from django.apps import apps
|
||||
from django.utils.module_loading import module_has_submodule
|
||||
|
||||
|
||||
def get_app_modules():
|
||||
"""
|
||||
Generator function that yields a module object for each installed app
|
||||
yields tuples of (app_name, module)
|
||||
"""
|
||||
for app in apps.get_app_configs():
|
||||
yield app.name, app.module
|
||||
|
||||
|
||||
def get_app_submodules(submodule_name):
|
||||
"""
|
||||
Searches each app module for the specified submodule
|
||||
yields tuples of (app_name, module)
|
||||
"""
|
||||
for name, module in get_app_modules():
|
||||
if module_has_submodule(module, submodule_name):
|
||||
yield name, import_module(f"{name}.{submodule_name}")
|
||||
82
env/lib/python3.10/site-packages/wagtail/utils/decorators.py
vendored
Normal file
82
env/lib/python3.10/site-packages/wagtail/utils/decorators.py
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import functools
|
||||
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
|
||||
# Need to inherit from object explicitly, to turn ``cached_classmethod`` in to
|
||||
# a new-style class. WeakKeyDictionary is an old-style class, which do not
|
||||
# support descriptors.
|
||||
class cached_classmethod(dict):
|
||||
"""
|
||||
Cache the result of a no-arg class method.
|
||||
.. code-block:: python
|
||||
class Foo:
|
||||
@cached_classmethod
|
||||
def bar(cls):
|
||||
# Some expensive computation
|
||||
return 'baz'
|
||||
Similar to ``@lru_cache``, but the cache is per-class, stores a single
|
||||
value, and thus doesn't fill up; where as ``@lru_cache`` is global across
|
||||
all classes, and could fill up if too many classes were used.
|
||||
"""
|
||||
|
||||
def __init__(self, fn):
|
||||
self.fn = fn
|
||||
functools.update_wrapper(self, fn)
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
"""Get the class_cache for this type when accessed"""
|
||||
return self[owner]
|
||||
|
||||
def __missing__(self, cls):
|
||||
"""Make a new class_cache on cache misses"""
|
||||
value = _cache(self, cls, self.fn)
|
||||
self[cls] = value
|
||||
return value
|
||||
|
||||
|
||||
class _cache:
|
||||
"""Calls the real class method behind when called, caching the result"""
|
||||
|
||||
def __init__(self, cache, cls, fn):
|
||||
self.cache = cache
|
||||
self.cls = cls
|
||||
self.fn = fn
|
||||
functools.update_wrapper(self, fn)
|
||||
|
||||
@cached_property
|
||||
def value(self):
|
||||
"""Generate the cached value"""
|
||||
return self.fn(self.cls)
|
||||
|
||||
def __call__(self):
|
||||
"""Get the cached value"""
|
||||
return self.value
|
||||
|
||||
def cache_clear(self):
|
||||
"""Clear the cached value."""
|
||||
# Named after lru_cache.cache_clear
|
||||
self.cache.pop(self.cls, None)
|
||||
|
||||
|
||||
def xframe_options_sameorigin_override(view_func):
|
||||
"""
|
||||
Modify a view function so its response has the X-Frame-Options HTTP header
|
||||
set to 'SAMEORIGIN'.
|
||||
|
||||
Adapted from Django's xframe_options_sameorigin so that it's always applied
|
||||
even if the response already has that header set:
|
||||
https://github.com/django/django/blob/3.2/django/views/decorators/clickjacking.py#L22-L37
|
||||
|
||||
Usage:
|
||||
@xframe_options_sameorigin_override
|
||||
def some_view(request):
|
||||
...
|
||||
"""
|
||||
|
||||
def wrapped_view(*args, **kwargs):
|
||||
resp = view_func(*args, **kwargs)
|
||||
resp["X-Frame-Options"] = "SAMEORIGIN"
|
||||
return resp
|
||||
|
||||
return functools.wraps(view_func)(wrapped_view)
|
||||
87
env/lib/python3.10/site-packages/wagtail/utils/deprecation.py
vendored
Normal file
87
env/lib/python3.10/site-packages/wagtail/utils/deprecation.py
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import warnings
|
||||
from importlib import import_module
|
||||
|
||||
|
||||
class RemovedInWagtail70Warning(DeprecationWarning):
|
||||
pass
|
||||
|
||||
|
||||
removed_in_next_version_warning = RemovedInWagtail70Warning
|
||||
|
||||
|
||||
class RemovedInWagtail80Warning(PendingDeprecationWarning):
|
||||
pass
|
||||
|
||||
|
||||
class MovedDefinitionHandler:
|
||||
"""
|
||||
A wrapper for module objects to enable definitions to be moved to a new module, with a
|
||||
deprecation path for the old location. Importing the name from the old location will
|
||||
raise a deprecation warning (but will still complete successfully).
|
||||
|
||||
To use, place the following code in the old module:
|
||||
|
||||
import sys
|
||||
from wagtail.utils.deprecation import MovedDefinitionHandler, RemovedInWagtailXWarning
|
||||
|
||||
MOVED_DEFINITIONS = {
|
||||
'SomeClassOrVariableName': 'path.to.new.module',
|
||||
}
|
||||
|
||||
sys.modules[__name__] = MovedDefinitionHandler(sys.modules[__name__], MOVED_DEFINITIONS, RemovedInWagtailXWarning)
|
||||
|
||||
If the name of the definition has also changed, you can specify its new name along with
|
||||
the path to its new module using a tuple. For example:
|
||||
|
||||
MOVED_DEFINITIONS = {
|
||||
'SomeClassOrVariableName': ('path.to.new.module', 'NewClassOrVariableName'),
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, real_module, moved_definitions, warning_class):
|
||||
self.real_module = real_module
|
||||
self.moved_definitions = moved_definitions
|
||||
self.warning_class = warning_class
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return getattr(self.real_module, name)
|
||||
except AttributeError as e:
|
||||
try:
|
||||
# is the missing name one of our moved definitions?
|
||||
new_module_name = self.moved_definitions[name]
|
||||
new_name = name
|
||||
|
||||
if isinstance(new_module_name, tuple):
|
||||
new_module_name, new_name = new_module_name
|
||||
|
||||
except KeyError:
|
||||
# raise the original AttributeError without including the inner try/catch
|
||||
# in the stack trace
|
||||
raise e from None
|
||||
|
||||
if new_name != name:
|
||||
warnings.warn(
|
||||
"%s has been moved from %s to %s and renamed to %s"
|
||||
% (name, self.real_module.__name__, new_module_name, new_name),
|
||||
category=self.warning_class,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
else:
|
||||
warnings.warn(
|
||||
"%s has been moved from %s to %s"
|
||||
% (name, self.real_module.__name__, new_module_name),
|
||||
category=self.warning_class,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
# load the requested definition from the module named in moved_definitions
|
||||
new_module = import_module(new_module_name)
|
||||
definition = getattr(new_module, new_name)
|
||||
|
||||
# stash that definition into the current module so that we don't have to
|
||||
# redo this import next time we access it
|
||||
setattr(self.real_module, name, definition)
|
||||
|
||||
return definition
|
||||
35
env/lib/python3.10/site-packages/wagtail/utils/file.py
vendored
Normal file
35
env/lib/python3.10/site-packages/wagtail/utils/file.py
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
import hashlib
|
||||
from io import UnsupportedOperation
|
||||
|
||||
HASH_READ_SIZE = 2**18 # 256k - matches `hashlib.file_digest`
|
||||
|
||||
|
||||
def hash_filelike(filelike):
|
||||
"""
|
||||
Compute the hash of a file-like object, without loading it all into memory.
|
||||
"""
|
||||
file_pos = 0
|
||||
if hasattr(filelike, "tell"):
|
||||
file_pos = filelike.tell()
|
||||
|
||||
try:
|
||||
# Reset file handler to the start of the file so we hash it all
|
||||
filelike.seek(0)
|
||||
except (AttributeError, UnsupportedOperation):
|
||||
pass
|
||||
|
||||
if hasattr(hashlib, "file_digest"):
|
||||
hasher = hashlib.file_digest(filelike, hashlib.sha1)
|
||||
else:
|
||||
hasher = hashlib.sha1()
|
||||
while True:
|
||||
data = filelike.read(HASH_READ_SIZE)
|
||||
if not data:
|
||||
break
|
||||
hasher.update(data)
|
||||
|
||||
if hasattr(filelike, "seek"):
|
||||
# Reset the file handler to where it was before
|
||||
filelike.seek(file_pos)
|
||||
|
||||
return hasher.hexdigest()
|
||||
14
env/lib/python3.10/site-packages/wagtail/utils/loading.py
vendored
Normal file
14
env/lib/python3.10/site-packages/wagtail/utils/loading.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
|
||||
def get_custom_form(form_setting):
|
||||
"""Return custom form class if defined and available"""
|
||||
try:
|
||||
return import_string(getattr(settings, form_setting))
|
||||
except ImportError:
|
||||
raise ImproperlyConfigured(
|
||||
"%s refers to a form '%s' that is not available"
|
||||
% (form_setting, getattr(settings, form_setting))
|
||||
)
|
||||
91
env/lib/python3.10/site-packages/wagtail/utils/registry.py
vendored
Normal file
91
env/lib/python3.10/site-packages/wagtail/utils/registry.py
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import models
|
||||
|
||||
from wagtail.coreutils import resolve_model_string
|
||||
|
||||
|
||||
class ObjectTypeRegistry:
|
||||
"""
|
||||
Implements a lookup table for mapping objects to values according to the object type.
|
||||
The most specific type according to the object's inheritance chain is selected.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# values in this dict will be returned if the field type exactly matches an item here
|
||||
self.values_by_exact_class = {}
|
||||
|
||||
# values in this dict will be returned if any class in the field's inheritance chain
|
||||
# matches, preferring more specific subclasses
|
||||
self.values_by_class = {}
|
||||
|
||||
def register(self, cls, value=None, exact_class=False):
|
||||
if exact_class:
|
||||
self.values_by_exact_class[cls] = value
|
||||
else:
|
||||
self.values_by_class[cls] = value
|
||||
|
||||
def get_by_type(self, cls):
|
||||
try:
|
||||
return self.values_by_exact_class[cls]
|
||||
except KeyError:
|
||||
for ancestor in cls.mro():
|
||||
try:
|
||||
return self.values_by_class[ancestor]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def get(self, obj):
|
||||
value = self.get_by_type(obj.__class__)
|
||||
|
||||
if callable(value) and not isinstance(value, type):
|
||||
value = value(obj)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class ModelFieldRegistry(ObjectTypeRegistry):
|
||||
"""
|
||||
Handles the recurring pattern where we need to register different values for different
|
||||
model field types, and retrieve the one that most closely matches a given model field,
|
||||
according to its type (taking inheritance into account), and in the case of foreign keys,
|
||||
the type of the related model (again, taking inheritance into account).
|
||||
|
||||
For example, this is used by wagtail.admin.forms.models when constructing model forms:
|
||||
we use such a registry to retrieve the appropriate dict of arguments to pass to the
|
||||
form field constructor. A lookup for a models.TextField will return a dict specifying a
|
||||
text area widget, and a lookup for a foreign key to Image will return a dict specifying
|
||||
an image chooser widget.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.values_by_class[models.ForeignKey] = self.foreign_key_lookup
|
||||
|
||||
# values in this dict will be returned if the field is a foreign key to a related
|
||||
# model in here, matching most specific subclass first
|
||||
self.values_by_fk_related_model = {}
|
||||
|
||||
def register(self, field_class, to=None, value=None, exact_class=False):
|
||||
if to:
|
||||
if field_class == models.ForeignKey:
|
||||
self.values_by_fk_related_model[resolve_model_string(to)] = value
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"The 'to' argument on ModelFieldRegistry.register is only valid for ForeignKey fields"
|
||||
)
|
||||
else:
|
||||
super().register(field_class, value=value, exact_class=exact_class)
|
||||
|
||||
def foreign_key_lookup(self, field):
|
||||
value = None
|
||||
target_model = field.remote_field.model
|
||||
|
||||
for model in target_model.mro():
|
||||
if model in self.values_by_fk_related_model:
|
||||
value = self.values_by_fk_related_model[model]
|
||||
break
|
||||
|
||||
if callable(value) and not isinstance(value, type):
|
||||
value = value(field)
|
||||
|
||||
return value
|
||||
106
env/lib/python3.10/site-packages/wagtail/utils/sendfile.py
vendored
Normal file
106
env/lib/python3.10/site-packages/wagtail/utils/sendfile.py
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
# Copied from django-sendfile 0.3.6 and tweaked to allow a backend to be passed
|
||||
# to sendfile()
|
||||
# See: https://github.com/johnsensible/django-sendfile/pull/33
|
||||
import os.path
|
||||
from mimetypes import guess_type
|
||||
|
||||
VERSION = (0, 3, 6)
|
||||
__version__ = ".".join(map(str, VERSION))
|
||||
|
||||
|
||||
def _lazy_load(fn):
|
||||
_cached = []
|
||||
|
||||
def _decorated():
|
||||
if not _cached:
|
||||
_cached.append(fn())
|
||||
return _cached[0]
|
||||
|
||||
def clear():
|
||||
while _cached:
|
||||
_cached.pop()
|
||||
|
||||
_decorated.clear = clear
|
||||
return _decorated
|
||||
|
||||
|
||||
@_lazy_load
|
||||
def _get_sendfile():
|
||||
from importlib import import_module
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
backend = getattr(settings, "SENDFILE_BACKEND", None)
|
||||
if not backend:
|
||||
raise ImproperlyConfigured("You must specify a value for SENDFILE_BACKEND")
|
||||
module = import_module(backend)
|
||||
return module.sendfile
|
||||
|
||||
|
||||
def sendfile(
|
||||
request,
|
||||
filename,
|
||||
attachment=False,
|
||||
attachment_filename=None,
|
||||
mimetype=None,
|
||||
encoding=None,
|
||||
backend=None,
|
||||
):
|
||||
"""
|
||||
create a response to send file using backend configured in SENDFILE_BACKEND
|
||||
|
||||
If attachment is True the content-disposition header will be set.
|
||||
This will typically prompt the user to download the file, rather
|
||||
than view it. The content-disposition filename depends on the
|
||||
value of attachment_filename:
|
||||
|
||||
None (default): Same as filename
|
||||
False: No content-disposition filename
|
||||
String: Value used as filename
|
||||
|
||||
If no mimetype or encoding are specified, then they will be guessed via the
|
||||
filename (using the standard python mimetypes module)
|
||||
"""
|
||||
_sendfile = backend or _get_sendfile()
|
||||
|
||||
if not os.path.exists(filename):
|
||||
from django.http import Http404
|
||||
|
||||
raise Http404('"%s" does not exist' % filename)
|
||||
|
||||
guessed_mimetype, guessed_encoding = guess_type(filename)
|
||||
if mimetype is None:
|
||||
if guessed_mimetype:
|
||||
mimetype = guessed_mimetype
|
||||
else:
|
||||
mimetype = "application/octet-stream"
|
||||
|
||||
response = _sendfile(request, filename, mimetype=mimetype)
|
||||
if attachment:
|
||||
parts = ["attachment"]
|
||||
else:
|
||||
parts = ["inline"]
|
||||
if attachment_filename is None:
|
||||
attachment_filename = os.path.basename(filename)
|
||||
if attachment_filename:
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from wagtail.coreutils import string_to_ascii
|
||||
|
||||
attachment_filename = force_str(attachment_filename)
|
||||
ascii_filename = string_to_ascii(attachment_filename)
|
||||
parts.append('filename="%s"' % ascii_filename)
|
||||
|
||||
if ascii_filename != attachment_filename:
|
||||
from urllib.parse import quote
|
||||
|
||||
quoted_filename = quote(attachment_filename)
|
||||
parts.append("filename*=UTF-8''%s" % quoted_filename)
|
||||
|
||||
response["Content-Disposition"] = "; ".join(parts)
|
||||
response["Content-length"] = os.path.getsize(filename)
|
||||
response["Content-Type"] = mimetype
|
||||
response["Content-Encoding"] = encoding or guessed_encoding
|
||||
|
||||
return response
|
||||
50
env/lib/python3.10/site-packages/wagtail/utils/sendfile_streaming_backend.py
vendored
Normal file
50
env/lib/python3.10/site-packages/wagtail/utils/sendfile_streaming_backend.py
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# Sendfile "streaming" backend
|
||||
# This is based on sendfiles builtin "simple" backend but uses a StreamingHttpResponse
|
||||
|
||||
import os
|
||||
import stat
|
||||
from email.utils import mktime_tz, parsedate_tz
|
||||
|
||||
from django.http import FileResponse, HttpResponseNotModified
|
||||
from django.utils.http import http_date
|
||||
|
||||
|
||||
def sendfile(request, filename, **kwargs):
|
||||
# Respect the If-Modified-Since header.
|
||||
statobj = os.stat(filename)
|
||||
|
||||
if not was_modified_since(
|
||||
request.headers.get("if-modified-since"),
|
||||
statobj[stat.ST_MTIME],
|
||||
):
|
||||
return HttpResponseNotModified()
|
||||
|
||||
response = FileResponse(open(filename, "rb"))
|
||||
|
||||
response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
|
||||
return response
|
||||
|
||||
|
||||
def was_modified_since(header=None, mtime=0):
|
||||
"""
|
||||
Was something modified since the user last downloaded it?
|
||||
|
||||
header
|
||||
This is the value of the If-Modified-Since header. If this is None,
|
||||
I'll just return True.
|
||||
|
||||
mtime
|
||||
This is the modification time of the item we're talking about.
|
||||
"""
|
||||
try:
|
||||
if header is None:
|
||||
raise ValueError
|
||||
header_date = parsedate_tz(header)
|
||||
if header_date is None:
|
||||
raise ValueError
|
||||
header_mtime = mktime_tz(header_date)
|
||||
if mtime > header_mtime:
|
||||
raise ValueError
|
||||
except (ValueError, OverflowError):
|
||||
return True
|
||||
return False
|
||||
90
env/lib/python3.10/site-packages/wagtail/utils/setup.py
vendored
Normal file
90
env/lib/python3.10/site-packages/wagtail/utils/setup.py
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from setuptools import Command
|
||||
from setuptools.command.bdist_egg import bdist_egg
|
||||
from setuptools.command.sdist import sdist as base_sdist
|
||||
|
||||
from wagtail import __semver__
|
||||
|
||||
|
||||
class assets_mixin:
|
||||
def compile_assets(self):
|
||||
try:
|
||||
subprocess.check_call(["npm", "run", "build"])
|
||||
except (OSError, subprocess.CalledProcessError) as e:
|
||||
print("Error compiling assets: " + str(e)) # noqa: T201
|
||||
raise SystemExit(1)
|
||||
|
||||
def publish_assets(self):
|
||||
try:
|
||||
subprocess.check_call(["npm", "publish", "client"])
|
||||
except (OSError, subprocess.CalledProcessError) as e:
|
||||
print("Error publishing front-end assets: " + str(e)) # noqa: T201
|
||||
raise SystemExit(1)
|
||||
|
||||
def bump_client_version(self):
|
||||
"""
|
||||
Writes the current Wagtail version number into package.json
|
||||
"""
|
||||
path = os.path.join(".", "client", "package.json")
|
||||
input_file = open(path)
|
||||
|
||||
try:
|
||||
package = json.loads(input_file.read().decode("utf-8"))
|
||||
except ValueError as e:
|
||||
print("Unable to read " + path + " " + e) # noqa: T201
|
||||
raise SystemExit(1)
|
||||
|
||||
package["version"] = __semver__
|
||||
|
||||
try:
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
f.write(str(json.dumps(package, indent=2, ensure_ascii=False)))
|
||||
except OSError as e:
|
||||
print( # noqa: T201
|
||||
"Error setting the version for front-end assets: " + str(e)
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
class assets(Command, assets_mixin):
|
||||
user_options = []
|
||||
|
||||
def initialize_options(self):
|
||||
pass
|
||||
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
self.bump_client_version()
|
||||
self.compile_assets()
|
||||
self.publish_assets()
|
||||
|
||||
|
||||
class sdist(base_sdist, assets_mixin):
|
||||
def run(self):
|
||||
self.compile_assets()
|
||||
base_sdist.run(self)
|
||||
|
||||
|
||||
class check_bdist_egg(bdist_egg):
|
||||
# If this file does not exist, warn the user to compile the assets
|
||||
sentinel_dir = "wagtail/wagtailadmin/static/"
|
||||
|
||||
def run(self):
|
||||
bdist_egg.run(self)
|
||||
if not os.path.isdir(self.sentinel_dir):
|
||||
print( # noqa: T201
|
||||
"\n".join(
|
||||
[
|
||||
"************************************************************",
|
||||
"The front end assets for Wagtail are missing.",
|
||||
"To generate the assets, please refer to the documentation in",
|
||||
"docs/contributing/developing.md",
|
||||
"************************************************************",
|
||||
]
|
||||
)
|
||||
)
|
||||
7
env/lib/python3.10/site-packages/wagtail/utils/text.py
vendored
Normal file
7
env/lib/python3.10/site-packages/wagtail/utils/text.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from bs4 import BeautifulSoup
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
|
||||
def text_from_html(val):
|
||||
# Return the unescaped text content of an HTML string
|
||||
return BeautifulSoup(force_str(val), "html.parser").getText().strip()
|
||||
43
env/lib/python3.10/site-packages/wagtail/utils/timestamps.py
vendored
Normal file
43
env/lib/python3.10/site-packages/wagtail/utils/timestamps.py
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import formats, timezone
|
||||
from django.utils.dateparse import parse_datetime
|
||||
|
||||
|
||||
def ensure_utc(value):
|
||||
"""
|
||||
Similar to how django-modelcluster stores the revision's data and similar to how
|
||||
django stores dates in the database, this converts the date to UTC if required.
|
||||
"""
|
||||
# https://github.com/wagtail/django-modelcluster/blob/8666f16eaf23ca98afc160b0a4729864411c0563/modelcluster/models.py#L21-L28
|
||||
if settings.USE_TZ:
|
||||
if timezone.is_naive(value):
|
||||
default_timezone = timezone.get_default_timezone()
|
||||
value = timezone.make_aware(value, default_timezone).astimezone(
|
||||
datetime.timezone.utc
|
||||
)
|
||||
else:
|
||||
# convert to UTC
|
||||
value = timezone.localtime(value, datetime.timezone.utc)
|
||||
return value
|
||||
|
||||
|
||||
def parse_datetime_localized(date_string):
|
||||
"""
|
||||
Uses Django's parse_datetime(), but ensures to return an aware datetime.
|
||||
"""
|
||||
dt = parse_datetime(date_string)
|
||||
if settings.USE_TZ and timezone.is_naive(dt):
|
||||
dt = timezone.make_aware(dt, timezone=timezone.get_default_timezone())
|
||||
return dt
|
||||
|
||||
|
||||
def render_timestamp(timestamp):
|
||||
"""
|
||||
Helper function to format a possibly-timezone-aware datetime into the format
|
||||
used by Django (e.g. in templates).
|
||||
"""
|
||||
if timezone.is_aware(timestamp):
|
||||
timestamp = timezone.localtime(timestamp)
|
||||
return formats.date_format(timestamp, "DATETIME_FORMAT")
|
||||
17
env/lib/python3.10/site-packages/wagtail/utils/urlpatterns.py
vendored
Normal file
17
env/lib/python3.10/site-packages/wagtail/utils/urlpatterns.py
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
from functools import update_wrapper
|
||||
|
||||
|
||||
def decorate_urlpatterns(urlpatterns, decorator):
|
||||
"""Decorate all the views in the passed urlpatterns list with the given decorator"""
|
||||
for pattern in urlpatterns:
|
||||
if hasattr(pattern, "url_patterns"):
|
||||
# this is an included RegexURLResolver; recursively decorate the views
|
||||
# contained in it
|
||||
decorate_urlpatterns(pattern.url_patterns, decorator)
|
||||
|
||||
if getattr(pattern, "callback", None):
|
||||
pattern.callback = update_wrapper(
|
||||
decorator(pattern.callback), pattern.callback
|
||||
)
|
||||
|
||||
return urlpatterns
|
||||
42
env/lib/python3.10/site-packages/wagtail/utils/utils.py
vendored
Normal file
42
env/lib/python3.10/site-packages/wagtail/utils/utils.py
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
from collections.abc import Mapping
|
||||
|
||||
|
||||
def deep_update(source, overrides):
|
||||
"""Update a nested dictionary or similar mapping.
|
||||
|
||||
Modify ``source`` in place.
|
||||
"""
|
||||
for key, value in overrides.items():
|
||||
if isinstance(value, Mapping) and value:
|
||||
returned = deep_update(source.get(key, {}), value)
|
||||
source[key] = returned
|
||||
else:
|
||||
source[key] = overrides[key]
|
||||
return source
|
||||
|
||||
|
||||
def flatten_choices(choices):
|
||||
"""
|
||||
Convert potentially grouped choices into a flat dict of choices.
|
||||
|
||||
flatten_choices([(1, '1st'), (2, '2nd')]) -> {1: '1st', 2: '2nd'}
|
||||
flatten_choices([('Group', [(1, '1st'), (2, '2nd')])]) -> {1: '1st', 2: '2nd'}
|
||||
flatten_choices({'Group': {'1': '1st', '2': '2nd'}}) -> {'1': '1st', '2': '2nd'}
|
||||
"""
|
||||
ret = {}
|
||||
|
||||
to_unpack = choices.items() if isinstance(choices, dict) else choices
|
||||
|
||||
for key, value in to_unpack:
|
||||
if isinstance(value, (list, tuple)):
|
||||
# grouped choices (category, sub choices)
|
||||
for sub_key, sub_value in value:
|
||||
ret[str(sub_key)] = sub_value
|
||||
elif isinstance(value, (dict)):
|
||||
# grouped choices using dict (category, sub choices)
|
||||
for sub_key, sub_value in value.items():
|
||||
ret[str(sub_key)] = sub_value
|
||||
else:
|
||||
# choice (key, display value)
|
||||
ret[str(key)] = value
|
||||
return ret
|
||||
54
env/lib/python3.10/site-packages/wagtail/utils/version.py
vendored
Normal file
54
env/lib/python3.10/site-packages/wagtail/utils/version.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# This file is heavily inspired by django.utils.version
|
||||
|
||||
|
||||
def get_version(version):
|
||||
"""Return a PEP 440-compliant version number from VERSION."""
|
||||
version = get_complete_version(version)
|
||||
|
||||
# Now build the two parts of the version number:
|
||||
# main = X.Y[.Z]
|
||||
# sub = .devN - for pre-alpha releases
|
||||
# | {a|b|rc}N - for alpha, beta, and rc releases
|
||||
|
||||
main = get_main_version(version)
|
||||
|
||||
sub = ""
|
||||
if version[3] != "final":
|
||||
mapping = {"alpha": "a", "beta": "b", "rc": "rc", "dev": ".dev"}
|
||||
sub = mapping[version[3]] + str(version[4])
|
||||
|
||||
return main + sub
|
||||
|
||||
|
||||
def get_main_version(version=None, include_patch=True):
|
||||
"""Return main version (X.Y[.Z]) from VERSION."""
|
||||
version = get_complete_version(version)
|
||||
if include_patch:
|
||||
parts = 2 if version[2] == 0 else 3
|
||||
else:
|
||||
parts = 2
|
||||
return ".".join(str(x) for x in version[:parts])
|
||||
|
||||
|
||||
def get_complete_version(version=None):
|
||||
"""
|
||||
Return a tuple of the Wagtail version. If version argument is non-empty,
|
||||
check for correctness of the tuple provided.
|
||||
"""
|
||||
if version is None:
|
||||
from wagtail import VERSION as version
|
||||
else:
|
||||
assert len(version) == 5
|
||||
assert version[3] in ("dev", "alpha", "beta", "rc", "final")
|
||||
|
||||
return version
|
||||
|
||||
|
||||
def get_semver_version(version):
|
||||
"Returns the semver version (X.Y.Z[-(alpha|beta)]) from VERSION"
|
||||
main = ".".join(str(x) for x in version[:3])
|
||||
|
||||
sub = ""
|
||||
if version[3] != "final":
|
||||
sub = "-{}.{}".format(*version[3:])
|
||||
return main + sub
|
||||
45
env/lib/python3.10/site-packages/wagtail/utils/widgets.py
vendored
Normal file
45
env/lib/python3.10/site-packages/wagtail/utils/widgets.py
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
from warnings import warn
|
||||
|
||||
from django.forms.widgets import Widget
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from wagtail.utils.deprecation import RemovedInWagtail70Warning
|
||||
|
||||
|
||||
class WidgetWithScript(Widget):
|
||||
warn(
|
||||
"The usage of `WidgetWithScript` hook is deprecated. Use external scripts instead.",
|
||||
category=RemovedInWagtail70Warning,
|
||||
)
|
||||
|
||||
def render_html(self, name, value, attrs):
|
||||
"""Render the HTML (non-JS) portion of the field markup"""
|
||||
return super().render(name, value, attrs)
|
||||
|
||||
def get_value_data(self, value):
|
||||
# Perform any necessary preprocessing on the value passed to render() before it is passed
|
||||
# on to render_html / render_js_init. This is a good place to perform database lookups
|
||||
# that are needed by both render_html and render_js_init. Return value is arbitrary
|
||||
# (we only care that render_html / render_js_init can accept it), but will typically be
|
||||
# a dict of data needed for rendering: id, title etc.
|
||||
return value
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
# no point trying to come up with sensible semantics for when 'id' is missing from attrs,
|
||||
# so let's make sure it fails early in the process
|
||||
try:
|
||||
id_ = attrs["id"]
|
||||
except (KeyError, TypeError):
|
||||
raise TypeError(
|
||||
"WidgetWithScript cannot be rendered without an 'id' attribute"
|
||||
)
|
||||
|
||||
value_data = self.get_value_data(value)
|
||||
widget_html = self.render_html(name, value_data, attrs)
|
||||
|
||||
js = self.render_js_init(id_, name, value_data)
|
||||
out = f"{widget_html}<script>{js}</script>"
|
||||
return mark_safe(out)
|
||||
|
||||
def render_js_init(self, id_, name, value):
|
||||
return ""
|
||||
Reference in New Issue
Block a user