"""Some various utilities."""
from __future__ import annotations
from os import remove
from os.path import join, exists, basename, splitext
from re import finditer as re_finditer, sub as re_sub
from base64 import b64encode
from json import load as json_load, dumps as json_dumps
from json.decoder import JSONDecodeError
from pyramid.request import Request
from cioprocessor.lib.utils import select_files
from cioprocessor.lib.pbuild import PBuild
from ciowarehouse2.lib.ciotype import CioType
from ciowarehouse2.lib.ciopath import CioPath
from ciowarehouse2.lib.warehouse import Warehouse
from .i18n import _, translate
# =============================================================================
[docs]
def special_protect(content: str) -> str:
"""Convert special characters (&, <, >) in a secure representation.
:param str content:
Content to protect.
:rtype: str
"""
return content\
.replace('&', '&').replace(' ', '‧')\
.replace('<', '<').replace('>', '>') \
if content else ''
# =============================================================================
[docs]
def special_unprotect(content: str) -> str:
"""Cancel the protection made by _special_protect().
:param str content:
Protected content.
:rtype: str
"""
return content\
.replace('&', '&').replace('‧', ' ')\
.replace('<', '<').replace('>', '>').replace('’', "'") \
if content else ''
# =============================================================================
[docs]
def image_base64(
pbuild: PBuild,
abs_path: str,
html: str,
remove_original: bool = True) -> str | None:
"""Replace image source with its content in base64.
:type pbuild: .lib.pbuild.PBuild
:param pbuild:
Current processor build object.
:param str abs_path:
Absolute path to current directory.
:param str html:
HTML to process.
:param remove_original: bool
If `True`, remove original image.
:rtype: str
"""
modified = False
to_remove = set()
for occur in re_finditer(r'src="([^\?"]+)\?cioreplace=base64"', html):
filename = join(abs_path, occur.group(1))
if not exists(filename):
pbuild.warning(
translate(
_('${f} does not exist.', {'f': occur.group(1)}),
pbuild.lang))
continue
with open(filename, 'rb') as hdl:
encoded = b64encode(hdl.read())
ext = {
'.png': 'png',
'.jpg': 'jpeg',
'.jpeg': 'jpeg',
'.svg': 'svg+xml',
'.gif': 'gif',
'.webp': 'webp'
}.get(splitext(filename)[1], 'png')
html = html.replace(
occur.group(0),
f'src="data:image/{ext};base64,{encoded.decode("utf8")}"')
if remove_original:
to_remove.add(filename)
modified = True
for filename in to_remove:
remove(filename)
return html if modified else None
# =============================================================================
[docs]
def remove_css_maps(pbuild: PBuild, step: dict):
"""Remove CSS maps and their call.
:type build: cioprocessor.lib.build.PBuild
:param build:
Current build object.
:param dict step:
Dictionary defining the current step.
"""
if not pbuild.current['values'].get('remove_css_map'):
return
if 'select' not in step:
step['select'] = '\\.css$'
for path in select_files(pbuild, step):
map_file = '{}.map'.format(path)
if exists(map_file):
remove(map_file)
with open(path, 'r', encoding='utf8') as hdl:
content = hdl.read()
content = re_sub('/\\*# sourceMappingURL=[^*]+\\*/', '', content)
with open(path, 'w', encoding='utf8') as hdl:
hdl.write(content)
# =============================================================================
[docs]
def ajust_css_maps(pbuild: PBuild, step: dict):
"""Ajust CSS maps and their call.
:type build: cioprocessor.lib.build.PBuild
:param build:
Current build object.
:param dict step:
Dictionary defining the current step.
"""
if 'select' not in step:
step['select'] = '\\.css$'
for path in select_files(pbuild, step):
with open(path, 'r', encoding='utf8') as hdl:
content = hdl.read()
content = re_sub(
'/\\*# sourceMappingURL=[^*]+\\*/',
f'/*# sourceMappingURL={basename(path)}.map */', content)
with open(path, 'w', encoding='utf8') as hdl:
hdl.write(content)
# =============================================================================
[docs]
def pretty_print_json(pbuild: PBuild, step: dict) -> bool:
"""Pretty print JSON files.
:type build: cioprocessor.lib.build.PBuild
:param build:
Current build object.
:param dict step:
Dictionary defining the current step.
:rtype: bool
"""
if 'select' not in step:
step['select'] = '\\.json$'
has_error = False
for path in select_files(pbuild, step):
with open(path, 'r', encoding='utf8') as hdl:
try:
json = json_load(hdl)
except (OSError, JSONDecodeError) as error:
pbuild.error(
translate(_('JSON: ${e}', {'e': error}), pbuild.lang))
has_error = True
continue
with open(path, 'w', encoding='utf8') as hdl:
hdl.write(json_dumps(json, indent=4, ensure_ascii=False))
return has_error
# =============================================================================
[docs]
def calling_file(
request: Request,
rendering_type: str | None = None
) -> tuple[Warehouse | None, CioPath | None, dict | None]:
"""Return warehouse, `CioPath` and rendering of the URL found in the
request.
:type request: pyramid.request.Request
:param request:
Current request.
:param str rendering_type: (optional)
Type of rendering ('viewing' or 'editing')
:rtype: tuple
"""
ciotype = CioType.from_request(request)
ciopath = CioPath.from_request(request)
ciowarehouse2 = request.registry['modules']['ciowarehouse2']
warehouse = ciowarehouse2.warehouse(request, ciopath.wid)
manager = ciowarehouse2.manager(request, ciotype)
if warehouse is None or manager is None:
return warehouse, ciopath, None
if rendering_type is None:
rendering_type = request.matchdict.get('rendering')
rendering = manager.current_rendering(request, warehouse, rendering_type)
return warehouse, ciopath, rendering