angrybeanie_wagtail/env/lib/python3.12/site-packages/wagtail/api/v2/utils.py
2025-07-25 21:32:16 +10:00

270 lines
8.2 KiB
Python

from urllib.parse import urlsplit
from django.conf import settings
from django.utils.encoding import force_str
from wagtail.coreutils import resolve_model_string
from wagtail.models import Page, Site
class BadRequestError(Exception):
pass
def get_base_url(request=None):
base_url = getattr(settings, "WAGTAILAPI_BASE_URL", None)
if base_url is None and request:
site = Site.find_for_request(request)
if site:
base_url = site.root_url
if base_url:
# We only want the scheme and netloc
base_url_parsed = urlsplit(force_str(base_url))
return base_url_parsed.scheme + "://" + base_url_parsed.netloc
def get_full_url(request, path):
if path.startswith(("http://", "https://")):
return path
base_url = get_base_url(request) or ""
return base_url + path
def get_object_detail_url(router, request, model, pk):
url_path = router.get_object_detail_urlpath(model, pk)
if url_path:
return get_full_url(request, url_path)
def page_models_from_string(string):
page_models = []
for sub_string in string.split(","):
page_model = resolve_model_string(sub_string)
if not issubclass(page_model, Page):
raise ValueError("Model is not a page")
page_models.append(page_model)
return tuple(page_models)
class FieldsParameterParseError(ValueError):
pass
def parse_fields_parameter(fields_str):
"""
Parses the ?fields= GET parameter. As this parameter is supposed to be used
by developers, the syntax is quite tight (eg, not allowing any whitespace).
Having a strict syntax allows us to extend the it at a later date with less
chance of breaking anyone's code.
This function takes a string and returns a list of tuples representing each
top-level field. Each tuple contains three items:
- The name of the field (string)
- Whether the field has been negated (boolean)
- A list of nested fields if there are any, None otherwise
Some examples of how this function works:
>>> parse_fields_parameter("foo")
[
('foo', False, None),
]
>>> parse_fields_parameter("foo,bar")
[
('foo', False, None),
('bar', False, None),
]
>>> parse_fields_parameter("-foo")
[
('foo', True, None),
]
>>> parse_fields_parameter("foo(bar,baz)")
[
('foo', False, [
('bar', False, None),
('baz', False, None),
]),
]
It raises a FieldsParameterParseError (subclass of ValueError) if it
encounters a syntax error
"""
def get_position(current_str):
return len(fields_str) - len(current_str)
def parse_field_identifier(fields_str):
first_char = True
negated = False
ident = ""
while fields_str:
char = fields_str[0]
if char in ["(", ")", ","]:
if not ident:
raise FieldsParameterParseError(
"unexpected char '%s' at position %d"
% (char, get_position(fields_str))
)
if ident in ["*", "_"] and char == "(":
# * and _ cannot have nested fields
raise FieldsParameterParseError(
"unexpected char '%s' at position %d"
% (char, get_position(fields_str))
)
return ident, negated, fields_str
elif char == "-":
if not first_char:
raise FieldsParameterParseError(
"unexpected char '%s' at position %d"
% (char, get_position(fields_str))
)
negated = True
elif char in ["*", "_"]:
if ident and char == "*":
raise FieldsParameterParseError(
"unexpected char '%s' at position %d"
% (char, get_position(fields_str))
)
ident += char
elif char.isalnum() or char == "_":
if ident == "*":
# * can only be on its own
raise FieldsParameterParseError(
"unexpected char '%s' at position %d"
% (char, get_position(fields_str))
)
ident += char
elif char.isspace():
raise FieldsParameterParseError(
"unexpected whitespace at position %d" % get_position(fields_str)
)
else:
raise FieldsParameterParseError(
"unexpected char '%s' at position %d"
% (char, get_position(fields_str))
)
first_char = False
fields_str = fields_str[1:]
return ident, negated, fields_str
def parse_fields(fields_str, expect_close_bracket=False):
first_ident = None
is_first = True
fields = []
while fields_str:
sub_fields = None
ident, negated, fields_str = parse_field_identifier(fields_str)
# Some checks specific to '*' and '_'
if ident in ["*", "_"]:
if not is_first:
raise FieldsParameterParseError(
"'%s' must be in the first position" % ident
)
if negated:
raise FieldsParameterParseError("'%s' cannot be negated" % ident)
if fields_str and fields_str[0] == "(":
if negated:
# Negated fields cannot contain subfields
raise FieldsParameterParseError(
"unexpected char '(' at position %d" % get_position(fields_str)
)
sub_fields, fields_str = parse_fields(
fields_str[1:], expect_close_bracket=True
)
if is_first:
first_ident = ident
else:
# Negated fields can't be used with '_'
if first_ident == "_" and negated:
# _,foo is allowed but _,-foo is not
raise FieldsParameterParseError(
"negated fields with '_' doesn't make sense"
)
# Additional fields without sub fields can't be used with '*'
if first_ident == "*" and not negated and not sub_fields:
# *,foo(bar) and *,-foo are allowed but *,foo is not
raise FieldsParameterParseError(
"additional fields with '*' doesn't make sense"
)
fields.append((ident, negated, sub_fields))
if fields_str and fields_str[0] == ")":
if not expect_close_bracket:
raise FieldsParameterParseError(
"unexpected char ')' at position %d" % get_position(fields_str)
)
return fields, fields_str[1:]
if fields_str and fields_str[0] == ",":
fields_str = fields_str[1:]
# A comma can not exist immediately before another comma or the end of the string
if not fields_str or fields_str[0] == ",":
raise FieldsParameterParseError(
"unexpected char ',' at position %d" % get_position(fields_str)
)
is_first = False
if expect_close_bracket:
# This parser should've exited with a close bracket but instead we
# hit the end of the input. Raise an error
raise FieldsParameterParseError(
"unexpected end of input (did you miss out a close bracket?)"
)
return fields, fields_str
fields, _ = parse_fields(fields_str)
return fields
def parse_boolean(value):
"""
Parses strings into booleans using the following mapping (case-sensitive):
'true' => True
'false' => False
'1' => True
'0' => False
"""
if value in ["true", "1"]:
return True
elif value in ["false", "0"]:
return False
else:
raise ValueError("expected 'true' or 'false', got '%s'" % value)