diff --git a/.ddev/apache/10.conf b/.ddev/apache/10.conf new file mode 100644 index 0000000..be59f2a --- /dev/null +++ b/.ddev/apache/10.conf @@ -0,0 +1,39 @@ + + ServerName xima-typo3-content-planner.ddev.site + DocumentRoot /var/www/html/.test + + AllowOverride All + Allow from All + + + RewriteEngine On + RewriteCond %{HTTP:X-Forwarded-Proto} =https + RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d + RewriteRule ^(.+[^/])$ https://%{HTTP_HOST}$1/ [redirect,last] + SetEnvIf X-Forwarded-Proto "https" HTTPS=on + ErrorLog /dev/stdout + CustomLog ${APACHE_LOG_DIR}/access.log combined + Alias "/phpstatus" "/var/www/phpstatus.php" + + + + ServerName xima-typo3-content-planner.ddev.site + DocumentRoot /var/www/html/.test + + AllowOverride All + Allow from All + + + RewriteEngine On + RewriteCond %{HTTP:X-Forwarded-Proto} =https + RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d + RewriteRule ^(.+[^/])$ https://%{HTTP_HOST}$1/ [redirect,last] + SetEnvIf X-Forwarded-Proto "https" HTTPS=on + ErrorLog /dev/stdout + CustomLog ${APACHE_LOG_DIR}/access.log combined + Alias "/phpstatus" "/var/www/phpstatus.php" + + SSLEngine on + SSLCertificateFile /etc/ssl/certs/master.crt + SSLCertificateKeyFile /etc/ssl/certs/master.key + diff --git a/.ddev/apache/20.conf b/.ddev/apache/20.conf new file mode 100644 index 0000000..a6a2970 --- /dev/null +++ b/.ddev/apache/20.conf @@ -0,0 +1,47 @@ + + ServerName sub.xima-typo3-content-planner.ddev.site + ServerAlias *.xima-typo3-content-planner.ddev.site + DocumentRoot /var/www/html/.test + + AllowOverride All + Allow from All + + + RewriteEngine On + RewriteCond %{HTTP_HOST} ^([a-z0-9-]+)\.xima-typo3-content-planner\.ddev\.site$ + RewriteRule ^(.*)$ /var/www/html/.test/%1/public/$1 [L] + + RewriteCond %{HTTP:X-Forwarded-Proto} =https + RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d + RewriteRule ^(.+[^/])$ https://%{HTTP_HOST}$1/ [redirect,last] + SetEnvIf X-Forwarded-Proto "https" HTTPS=on + ErrorLog /dev/stdout + CustomLog ${APACHE_LOG_DIR}/access.log combined + Alias "/phpstatus" "/var/www/phpstatus.php" + + + + ServerName sub.xima-typo3-content-planner.ddev.site + ServerAlias *.xima-typo3-content-planner.ddev.site + DocumentRoot /var/www/html/.test + + AllowOverride All + Allow from All + + + RewriteEngine On + RewriteCond %{HTTP_HOST} ^([a-z0-9-]+)\.xima-typo3-content-planner\.ddev\.site$ + RewriteRule ^(.*)$ /var/www/html/.test/%1/public/$1 [L] + + RewriteCond %{HTTP:X-Forwarded-Proto} =https + RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d + RewriteRule ^(.+[^/])$ https://%{HTTP_HOST}$1/ [redirect,last] + SetEnvIf X-Forwarded-Proto "https" HTTPS=on + ErrorLog /dev/stdout + CustomLog ${APACHE_LOG_DIR}/access.log combined + Alias "/phpstatus" "/var/www/phpstatus.php" + + SSLEngine on + SSLCertificateFile /etc/ssl/certs/master.crt + SSLCertificateKeyFile /etc/ssl/certs/master.key + diff --git a/.ddev/apache/apache-site.conf b/.ddev/apache/apache-site.conf new file mode 100644 index 0000000..d53c5d9 --- /dev/null +++ b/.ddev/apache/apache-site.conf @@ -0,0 +1,100 @@ +# ddev generic/default/php config for apache2 + +#ddev-generated +# If you want to take over this file and customize it, remove the line above +# and ddev will respect it and won't overwrite the file. +# See https://ddev.readthedocs.io/en/stable/users/extend/customization-extendibility/#custom-apache-configuration + + RewriteEngine On + RewriteCond %{HTTP:X-Forwarded-Proto} =https + RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d + RewriteRule ^(.+[^/])$ https://%{HTTP_HOST}$1/ [redirect,last] + + SetEnvIf X-Forwarded-Proto "https" HTTPS=on + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html/.test + + AllowOverride All + Allow from All + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog /dev/stdout + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf + + # Increase allowed field size for large cookies header. + LimitRequestFieldSize 16380 + + # Simple ddev technique to get a phpstatus + Alias "/phpstatus" "/var/www/phpstatus.php" + Alias "/xhprof" "/var/xhprof/xhprof_html" + + Options Indexes + AllowOverride None + Require all granted + + + + + + SSLEngine on + SSLCertificateFile /etc/ssl/certs/master.crt + SSLCertificateKeyFile /etc/ssl/certs/master.key + + # Workaround from https://mail-archives.apache.org/mod_mbox/httpd-users/201403.mbox/%3C49404A24C7FAD94BB7B45E86A9305F6214D04652@MSGEXSV21103.ent.wfb.bank.corp%3E + # See also https://gist.github.com/nurtext/b6ac07ac7d8c372bc8eb + + RewriteEngine On + RewriteCond %{HTTP:X-Forwarded-Proto} =https + RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d + RewriteRule ^(.+[^/])$ https://%{HTTP_HOST}$1/ [redirect,last] + + SetEnvIf X-Forwarded-Proto "https" HTTPS=on + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html/.test + + AllowOverride All + Allow from All + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog /dev/stdout + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # Increase allowed field size for large cookies header. + LimitRequestFieldSize 16380 + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf + # Simple ddev technique to get a phpstatus + Alias "/phpstatus" "/var/www/phpstatus.php" + Alias "/xhprof" "/var/xhprof/xhprof_html" + + Options Indexes + AllowOverride None + Require all granted + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/.ddev/commands/web/.install-11 b/.ddev/commands/web/.install-11 new file mode 100755 index 0000000..11bcb51 --- /dev/null +++ b/.ddev/commands/web/.install-11 @@ -0,0 +1,30 @@ +#!/bin/bash + +. .ddev/commands/web/.utils.sh + +VERSION=11 + +rm -rf /var/www/html/.test/$VERSION/* + +message red "ToDo" +exit 0 + +intro_typo3 $VERSION +install_start $VERSION +export TYPO3_BIN="$BASE_PATH/vendor/bin/typo3cms" + +composer req typo3/cms-base-distribution:'^11.5' helhum/typo3-console:'^7.1' $PACKAGE_NAME:'*@dev' \ + --no-progress -n -d /var/www/html/.test/$VERSION + +cd $BASE_PATH +TYPO3_INSTALL_DB_DBNAME=$DATABASE +$TYPO3_BIN install:setup -n --database-name $DATABASE +setup_typo3 +$TYPO3_BIN configuration:set 'GFX/processor_path_lzw' '/usr/bin/' + +sed -i "/'deprecations'/,/^[[:space:]]*'disabled' => true,/s/'disabled' => true,/'disabled' => false,/" /var/www/html/.test/$VERSION/public/typo3conf/LocalConfiguration.php + +sed -i -e "s/base: ht\//base: \//g" /var/www/html/.test/$VERSION/config/sites/main/config.yaml +sed -i -e 's/base: \/en\//base: \//g' /var/www/html/.test/$VERSION/config/sites/main/config.yaml + +update_typo3 diff --git a/.ddev/commands/web/.install-12 b/.ddev/commands/web/.install-12 new file mode 100755 index 0000000..8cbfd20 --- /dev/null +++ b/.ddev/commands/web/.install-12 @@ -0,0 +1,25 @@ +#!/bin/bash + +. .ddev/commands/web/.utils.sh + +VERSION=12 + +rm -rf /var/www/html/.test/$VERSION/* +intro_typo3 $VERSION +install_start $VERSION + +composer req typo3/cms-base-distribution:'^12.4' helhum/typo3-console:'^8.1' $PACKAGE_NAME:'*@dev' test/sitepackage:'*@dev' \ + --no-progress -n -d /var/www/html/.test/$VERSION + +cd $BASE_PATH +TYPO3_INSTALL_DB_DBNAME=$DATABASE +$TYPO3_BIN install:setup -n --database-name $DATABASE +setup_typo3 + +sed -i "/'deprecations'/,/^[[:space:]]*'disabled' => true,/s/'disabled' => true,/'disabled' => false,/" /var/www/html/.test/$VERSION/config/system/settings.php + +sed -i -e "s/base: ht\//base: \//g" /var/www/html/.test/$VERSION/config/sites/main/config.yaml +sed -i -e 's/base: \/en\//base: \//g' /var/www/html/.test/$VERSION/config/sites/main/config.yaml + +import_data +update_typo3 diff --git a/.ddev/commands/web/.install-13 b/.ddev/commands/web/.install-13 new file mode 100755 index 0000000..8e9902b --- /dev/null +++ b/.ddev/commands/web/.install-13 @@ -0,0 +1,23 @@ +#!/bin/bash + +. .ddev/commands/web/.utils.sh + +VERSION=13 + +rm -rf /var/www/html/.test/$VERSION/* +intro_typo3 $VERSION +install_start $VERSION + +composer req typo3/cms-base-distribution:'^13.4' helhum/typo3-console:'^8.2.1' $PACKAGE_NAME:'*@dev' test/sitepackage:'*@dev' \ + --no-progress -n -d /var/www/html/.test/$VERSION + +cd $BASE_PATH +TYPO3_INSTALL_DB_DBNAME=$DATABASE +mysql -h db -u root -p"root" -e "CREATE DATABASE $DATABASE;" +$TYPO3_BIN setup -n --dbname=$DATABASE --password=$TYPO3_DB_PASSWORD --create-site="https://${VERSION}.xima-typo3-frontend-edit.ddev.site" --admin-user-password=$TYPO3_SETUP_ADMIN_PASSWORD +setup_typo3 + +sed -i "/'deprecations'/,/^[[:space:]]*'disabled' => true,/s/'disabled' => true,/'disabled' => false,/" /var/www/html/.test/$VERSION/config/system/settings.php + +import_data +update_typo3 diff --git a/.ddev/commands/web/.utils.sh b/.ddev/commands/web/.utils.sh new file mode 100644 index 0000000..1d901ef --- /dev/null +++ b/.ddev/commands/web/.utils.sh @@ -0,0 +1,165 @@ +#!/bin/bash + +function get_lowest_supported_typo3_versions() { + local TYPO3_VERSIONS_ARRAY=() + IFS=' ' read -r -a TYPO3_VERSIONS_ARRAY <<< "$TYPO3_VERSIONS" + if [ ${#TYPO3_VERSIONS_ARRAY[@]} -eq 0 ]; then + message red "Error! No supported TYPO3 versions found in environment variables." + exit 1 + fi + printf "%s\n" "${TYPO3_VERSIONS_ARRAY[@]}" | sort -V | head -n 1 +} + +function get_supported_typo3_versions() { + if [ -z "${TYPO3_VERSIONS+x}" ]; then + message red "TYPO3_VERSIONS is unset. Please set it before running this function." + return 1 + else + local TYPO3_VERSIONS_ARRAY=() + IFS=' ' read -r -a TYPO3_VERSIONS_ARRAY <<< "$TYPO3_VERSIONS" + if [ ${#TYPO3_VERSIONS_ARRAY[@]} -eq 0 ]; then + message red "Error! No supported TYPO3 versions found in environment variables." + return 1 + fi + printf "%s\n" "${TYPO3_VERSIONS_ARRAY[@]}" + fi +} + +function check_typo3_version() { + local TYPO3=$1 + local SUPPORTED_TYPO3_VERSIONS=() + local found=0 + + if [ -z "$TYPO3" ]; then + message red "No TYPO3 version provided. Please set one of the supported TYPO3 versions as argument: $(get_supported_typo3_versions_comma_separated)" + exit 1 + fi + + while IFS= read -r line; do + SUPPORTED_TYPO3_VERSIONS+=("$line") + done < <(get_supported_typo3_versions) + + for version in "${SUPPORTED_TYPO3_VERSIONS[@]}"; do + if [[ "$version" == "$TYPO3" ]]; then + found=1 + break + fi + done + + if [[ $found -eq 0 ]]; then + message red "TYPO3 version '$TYPO3' is not supported." + exit 1 + fi + + return 0 +} + +function intro_typo3() { + local version=$1 + message magenta "-------------------------------------------------" + message magenta "|\t\t\t\t\t\t|" + message magenta "| \t\t TYPO3 $version \t\t|" + message magenta "|\t\t\t\t\t\t|" + message magenta "-------------------------------------------------" +} + +function install_start() { + local version=$1 + rm -rf /var/www/html/.test/$version/* + setup_environment $version + create_symlinks_main_extension + create_symlinks_additional_extensions + setup_composer +} + +function setup_environment() { + local version=$1 + BASE_PATH="/var/www/html/.test/$version" + rm -rf "$BASE_PATH" + mkdir -p "$BASE_PATH/packages/$EXTENSION_KEY" + chmod 775 -R $BASE_PATH + export DATABASE="database_$version" + export BASE_PATH + export VERSION="$version" + export TYPO3_BIN="$BASE_PATH/vendor/bin/typo3" + mysql -uroot -proot -e "DROP DATABASE IF EXISTS $DATABASE" +} + +function create_symlinks_main_extension() { + local exclusions=(".*" "Documentation" "Documentation-GENERATED-temp" "var") + for item in ./*; do + local base_name=$(basename "$item") + for exclusion in "${exclusions[@]}"; do + if [[ $base_name == "$exclusion" ]]; then + continue 2 + fi + done + ln -sr "$item" "$BASE_PATH/packages/$EXTENSION_KEY/$base_name" + done +} + +function create_symlinks_additional_extensions() { + for dir in .ddev/test/packages/*/; do + ln -sr "$dir" "$BASE_PATH/packages/$(basename "$dir")" + done +} + +function setup_composer() { + composer init --name="xima/typo3-$VERSION" --description="TYPO3 $VERSION" --no-interaction --working-dir "$BASE_PATH" + composer config extra.typo3/cms.web-dir public --working-dir "$BASE_PATH" + composer config repositories.packages path 'packages/*' --working-dir "$BASE_PATH" + composer config --no-interaction allow-plugins.typo3/cms-composer-installers true --working-dir "$BASE_PATH" + composer config --no-interaction allow-plugins.typo3/class-alias-loader true --working-dir "$BASE_PATH" +} + +function setup_typo3() { + cd $BASE_PATH + export TYPO3_INSTALL_DB_DBNAME=$DATABASE + $TYPO3_BIN configuration:set 'BE/debug' 1 + $TYPO3_BIN configuration:set 'FE/debug' 1 + $TYPO3_BIN configuration:set 'SYS/devIPmask' '*' + $TYPO3_BIN configuration:set 'SYS/displayErrors' 1 + $TYPO3_BIN configuration:set 'SYS/trustedHostsPattern' '.*.*' + $TYPO3_BIN configuration:set 'MAIL/transport' 'smtp' + $TYPO3_BIN configuration:set 'MAIL/transport_smtp_server' 'localhost:1025' + $TYPO3_BIN configuration:set 'GFX/processor' 'ImageMagick' + $TYPO3_BIN configuration:set 'GFX/processor_path' '/usr/bin/' +} + +function update_typo3() { + $TYPO3_BIN database:updateschema + $TYPO3_BIN cache:flush +} + +function import_data() { + $TYPO3_BIN database:import < /var/www/html/.ddev/test/data/dump.sql +} + +message() { + local color=$1 + local message=$2 + + case $color in + red) + echo -e "\033[31m$message\033[0m" + ;; + green) + echo -e "\033[32m$message\033[0m" + ;; + yellow) + echo -e "\033[33m$message\033[0m" + ;; + blue) + echo -e "\033[34m$message\033[0m" + ;; + magenta) + echo -e "\033[35m$message\033[0m" + ;; + cyan) + echo -e "\033[36m$message\033[0m" + ;; + *) + echo -e "$message" + ;; + esac +} diff --git a/.ddev/commands/web/11 b/.ddev/commands/web/11 new file mode 100755 index 0000000..fd03ae3 --- /dev/null +++ b/.ddev/commands/web/11 @@ -0,0 +1,20 @@ +#!/bin/bash + +## Description: Exec command for TYPO3 instance 11. +## Usage: 11 +## Example: "ddev 11 composer du -o" + +. .ddev/commands/web/.utils.sh + +command=$@ +version=11 + +TYPO3_PATH=".test/${version}" +if [ -f "$TYPO3_PATH" ]; then + message magenta "[TYPO3 v${version}] ${command}" + cd $TYPO3_PATH + $command +else + message red "TYPO3 binary not found for version ${version}" +fi + diff --git a/.ddev/commands/web/12 b/.ddev/commands/web/12 new file mode 100755 index 0000000..ad5b993 --- /dev/null +++ b/.ddev/commands/web/12 @@ -0,0 +1,20 @@ +#!/bin/bash + +## Description: Exec command for TYPO3 instance 12. +## Usage: 12 +## Example: "ddev 12 composer du -o" + +. .ddev/commands/web/.utils.sh + +command=$@ +version=12 + +TYPO3_PATH=".test/${version}" +if [ -f "$TYPO3_PATH" ]; then + message magenta "[TYPO3 v${version}] ${command}" + cd $TYPO3_PATH + $command +else + message red "TYPO3 binary not found for version ${version}" +fi + diff --git a/.ddev/commands/web/13 b/.ddev/commands/web/13 new file mode 100755 index 0000000..f762f89 --- /dev/null +++ b/.ddev/commands/web/13 @@ -0,0 +1,20 @@ +#!/bin/bash + +## Description: Exec command for TYPO3 instance 13. +## Usage: 13 +## Example: "ddev 13 composer du -o" + +. .ddev/commands/web/.utils.sh + +command=$@ +version=13 + +TYPO3_PATH=".test/${version}" +if [ -d "$TYPO3_PATH" ]; then + message magenta "[TYPO3 v${version}] ${command}" + cd $TYPO3_PATH + $command +else + message red "TYPO3 binary not found for version ${version}" +fi + diff --git a/.ddev/commands/web/install b/.ddev/commands/web/install new file mode 100755 index 0000000..fec0c08 --- /dev/null +++ b/.ddev/commands/web/install @@ -0,0 +1,25 @@ +#!/bin/bash + +## Description: Install TYPO3 instances. +## Usage: install +## Example: "ddev install" or "ddev install 12" + +TYPO3=${1} + +. .ddev/commands/web/.utils.sh + +if [ "$TYPO3" == "all" ]; then + mapfile -t versions < <(get_supported_typo3_versions) + for version in "${versions[@]}"; do + .ddev/commands/web/.install-$version + done +else + if [ -z "$TYPO3" ]; then + TYPO3=$(get_lowest_supported_typo3_versions) + else + if ! check_typo3_version "$TYPO3"; then + exit 1 + fi + fi + ".ddev/commands/web/.install-$TYPO3" +fi diff --git a/.ddev/commands/web/typo3 b/.ddev/commands/web/typo3 new file mode 100755 index 0000000..429ad2e --- /dev/null +++ b/.ddev/commands/web/typo3 @@ -0,0 +1,35 @@ +#!/bin/bash + +## Description: Exec TYPO3 command for all available TYPO3 integration instances or a selected TYPO3 instance. +## Usage: typo3 +## Example: "ddev typo3 cache:flush" or "ddev typo3 database:schemaupdate" or "ddev typo3 11 cache:flush" + +. .ddev/commands/web/.utils.sh + +command=$1 +mapfile -t versions < <(get_supported_typo3_versions) + +if [[ "$command" =~ ^[0-9]+$ ]] && [[ " ${versions[@]} " =~ " ${command} " ]]; then + version=$command + command=$2 + TYPO3_PATH=".test/${version}/vendor/bin/typo3" + if [ -f "$TYPO3_PATH" ]; then + message magenta "[TYPO3 v${version}] ${command}" + /usr/bin/php $TYPO3_PATH $command + else + message red "TYPO3 binary not found for version ${version}" + fi +else + for version in "${versions[@]}"; do + TYPO3_PATH=".test/${version}/vendor/bin/typo3" + if [ -f "$TYPO3_PATH" ]; then + message magenta "[TYPO3 v${version}] ${command}" + /usr/bin/php $TYPO3_PATH $command + else + message red "TYPO3 binary not found for version ${version}" + fi + done +fi + + + diff --git a/.ddev/config.yaml b/.ddev/config.yaml index ecc26d6..aa16b58 100644 --- a/.ddev/config.yaml +++ b/.ddev/config.yaml @@ -1,41 +1,26 @@ name: xima-typo3-content-planner -type: typo3 -docroot: public +type: php +docroot: .test php_version: "8.2" webserver_type: apache-fpm router_http_port: "80" router_https_port: "443" xdebug_enabled: false -additional_hostnames: [] +additional_hostnames: + - 11.xima-typo3-content-planner + - 12.xima-typo3-content-planner + - 13.xima-typo3-content-planner additional_fqdns: [] database: type: mariadb version: "10.4" webimage_extra_packages: [libxml2-utils] use_dns_when_possible: true -composer_version: "2" -web_environment: - - TYPO3_CONTEXT=Development/Local - - TYPO3_INSTALL_DB_DRIVER=mysqli - - TYPO3_INSTALL_DB_USER=db - - TYPO3_INSTALL_DB_PASSWORD=db - - TYPO3_INSTALL_DB_HOST=db - - TYPO3_INSTALL_DB_PORT=3306 - - TYPO3_INSTALL_DB_UNIX_SOCKET= - - TYPO3_INSTALL_DB_USE_EXISTING=1 - - TYPO3_INSTALL_DB_DBNAME=db - - TYPO3_INSTALL_ADMIN_USER=admin - - TYPO3_INSTALL_ADMIN_PASSWORD=Password1! - - TYPO3_INSTALL_SITE_NAME=Frontend Edit - - TYPO3_INSTALL_SITE_SETUP_TYPE=no - - TYPO3_INSTALL_SITE_BASE_URL= - - TYPO3_INSTALL_WEB_SERVER_CONFIG=apache -nodejs_version: "16" corepack_enable: false hooks: post-start: - exec: composer install --ignore-platform-reqs --no-scripts - - exec: if [ ! -f /var/www/html/config/system/settings.php ]; then vendor/bin/typo3 install:setup --force && vendor/bin/typo3 configuration:set SYS/trustedHostsPattern ".*"; fi + - exec: mkdir -p /var/www/html/.test/ && cp /var/www/html/.ddev/templates/* /var/www/html/.test/ # Key features of DDEV's config.yaml: diff --git a/.ddev/docker-compose.web.yaml b/.ddev/docker-compose.web.yaml new file mode 100644 index 0000000..dfa022d --- /dev/null +++ b/.ddev/docker-compose.web.yaml @@ -0,0 +1,31 @@ +services: + web: + environment: + - EXTENSION_KEY=xima_typo3_content_planner + - EXTENSION_NAME=xima-typo3-content-planner + - PACKAGE_NAME=xima/xima-typo3-content-planner + - TYPO3_VERSIONS=11 12 13 + + # TYPO3 v11 and v12 config + - TYPO3_INSTALL_DB_DRIVER=mysqli + - TYPO3_INSTALL_DB_USER=root + - TYPO3_INSTALL_DB_PASSWORD=root + - TYPO3_INSTALL_DB_HOST=db + - TYPO3_INSTALL_DB_UNIX_SOCKET= + - TYPO3_INSTALL_DB_USE_EXISTING=0 + - TYPO3_INSTALL_ADMIN_USER=admin + - TYPO3_INSTALL_ADMIN_PASSWORD=Password1! + - TYPO3_INSTALL_SITE_NAME=EXT:xima-typo3-content-planner Dev Environment + - TYPO3_INSTALL_SITE_SETUP_TYPE=site + - TYPO3_INSTALL_WEB_SERVER_CONFIG=apache + + # TYPO3 v13 config + - TYPO3_DB_DRIVER=mysqli + - TYPO3_DB_USERNAME=root + - TYPO3_DB_PASSWORD=root + - TYPO3_DB_HOST=db + - TYPO3_SETUP_ADMIN_EMAIL=admin@example.com + - TYPO3_SETUP_ADMIN_USERNAME=admin + - TYPO3_SETUP_ADMIN_PASSWORD=Password1! + - TYPO3_PROJECT_NAME=EXT:xima-typo3-content-planner Dev Environment + - TYPO3_SERVER_TYPE=apache diff --git a/.ddev/templates/index.php b/.ddev/templates/index.php new file mode 100644 index 0000000..d4d6347 --- /dev/null +++ b/.ddev/templates/index.php @@ -0,0 +1,90 @@ + + + + + + <?php echo($extensionKey); ?> + + + +
+

+ +

+
+
+
+
+

Run ddev install all to install all TYPO3 instances below:

+ {$version} https://{$version}.{$extensionKey}.ddev.site/typo3"; + } else { + echo "
Version {$version} is not installed. Run ddev install {$version} to install.
"; + } + } + ?> +

Additional information

+
+

TYPO3 Backend Credentials

+ +
+

DDEV commands

+ getPathname(); + $fileName = $fileInfo->getFilename(); + + if ($fileName[0] === '.' || $fileInfo->isDir()) { + continue; + } + + $fileContent = file($filePath); + if (strpos($fileContent[0], '#!/bin/bash') === 0) { + $description = ''; + $usage = ''; + $example = ''; + + foreach ($fileContent as $line) { + if (strpos($line, '## Description:') === 0) { + $description = trim(str_replace('## Description:', '', $line)); + } elseif (strpos($line, '## Usage:') === 0) { + $usage = trim(str_replace('## Usage:', '', $line)); + } elseif (strpos($line, '## Example:') === 0) { + $example = trim(str_replace('## Example:', '', $line)); + } + } + + echo "
ddev $usage
$description
Example: $example
"; + } + } + ?> +
+ + + diff --git a/.ddev/test/data/dump.sql b/.ddev/test/data/dump.sql new file mode 100644 index 0000000..c32ca38 --- /dev/null +++ b/.ddev/test/data/dump.sql @@ -0,0 +1,24 @@ +# pages + +INSERT INTO `pages` (`uid`, `pid`, `tstamp`, `crdate`, `deleted`, `hidden`, `starttime`, `endtime`, `fe_group`, `sorting`, `rowDescription`, `editlock`, `sys_language_uid`, `l10n_parent`, `l10n_source`, `l10n_state`, `l10n_diffsource`, `t3ver_oid`, `t3ver_wsid`, `t3ver_state`, `t3ver_stage`, `perms_userid`, `perms_groupid`, `perms_user`, `perms_group`, `perms_everybody`, `title`, `doktype`, `TSconfig`, `is_siteroot`, `php_tree_stop`, `url`, `shortcut`, `shortcut_mode`, `subtitle`, `layout`, `target`, `media`, `keywords`, `cache_timeout`, `cache_tags`, `description`, `no_search`, `SYS_LASTCHANGED`, `abstract`, `module`, `extendToSubpages`, `author`, `author_email`, `nav_title`, `nav_hide`, `content_from_pid`, `mount_pid`, `mount_pid_ol`, `l18n_cfg`, `backend_layout`, `backend_layout_next_level`, `tsconfig_includes`, `categories`, `lastUpdated`, `newUntil`, `slug`, `tx_impexp_origuid`, `seo_title`, `no_index`, `no_follow`, `og_title`, `og_description`, `og_image`, `twitter_title`, `twitter_description`, `twitter_image`, `twitter_card`, `canonical_link`, `sitemap_priority`, `sitemap_changefreq`, `tx_ximatypo3contentplanner_status`, `tx_ximatypo3contentplanner_assignee`, `tx_ximatypo3contentplanner_comments`) +VALUES + ('2', '1', '1736359157', '1736359138', '0', '0', '0', '0', '0', '256', NULL, '0', '0', '0', '0', '0', X'7B2268696464656E223A22227D', '0', '0', '0', '0', '1', '0', '31', '27', '0', 'Projects', '1', NULL, '0', '0', '', '0', '0', '', '0', '', '0', NULL, '0', '', NULL, '0', '0', NULL, '', '0', '', '', '', '0', '0', '0', '0', '0', '', '', NULL, '0', '0', '0', '/projects', '0', '', '0', '0', '', NULL, '0', '', NULL, '0', 'summary', '', '0.5', '', NULL, NULL, '0'), + ('3', '1', '1736359159', '1736359143', '0', '0', '0', '0', '0', '512', NULL, '0', '0', '0', '0', '0', X'7B2268696464656E223A22227D', '0', '0', '0', '0', '1', '0', '31', '27', '0', 'Team', '1', NULL, '0', '0', '', '0', '0', '', '0', '', '0', NULL, '0', '', NULL, '0', '0', NULL, '', '0', '', '', '', '0', '0', '0', '0', '0', '', '', NULL, '0', '0', '0', '/team', '0', '', '0', '0', '', NULL, '0', '', NULL, '0', 'summary', '', '0.5', '', NULL, NULL, '0'), + ('4', '1', '1736359155', '1736359150', '0', '0', '0', '0', '0', '128', NULL, '0', '0', '0', '0', '0', X'7B2268696464656E223A22227D', '0', '0', '0', '0', '1', '0', '31', '27', '0', 'Services', '1', NULL, '0', '0', '', '0', '0', '', '0', '', '0', NULL, '0', '', NULL, '0', '0', NULL, '', '0', '', '', '', '0', '0', '0', '0', '0', '', '', NULL, '0', '0', '0', '/services', '0', '', '0', '0', '', NULL, '0', '', NULL, '0', 'summary', '', '0.5', '', NULL, NULL, '0'), + ('5', '1', '1736359233', '1736359165', '0', '0', '0', '0', '', '768', NULL, '0', '0', '0', '0', '0', X'7B2268696464656E223A22227D', '0', '0', '0', '0', '1', '0', '31', '27', '0', 'Contact', '1', NULL, '0', '0', '', '0', '0', '', '0', '', '0', NULL, '0', '', NULL, '0', '0', NULL, '', '0', '', '', '', '0', '0', '0', '0', '0', '', '', NULL, '0', '0', '0', '/contact', '0', '', '0', '0', '', NULL, '0', '', NULL, '0', 'summary', '', '0.5', '', NULL, NULL, '0'), + ('6', '1', '1736359288', '1736359286', '0', '0', '0', '0', '0', '1024', NULL, '0', '0', '0', '0', '0', X'7B2268696464656E223A22227D', '0', '0', '0', '0', '1', '0', '31', '27', '0', 'Categories', '254', NULL, '0', '0', '', '0', '0', '', '0', '', '0', NULL, '0', '', NULL, '0', '0', NULL, '', '0', '', '', '', '0', '0', '0', '0', '0', '', '', NULL, '0', '0', '0', '/categories', '0', '', '0', '0', '', NULL, '0', '', NULL, '0', 'summary', '', '0.5', '', NULL, NULL, '0'); + +# tt_content + +INSERT INTO `tt_content` (`uid`, `rowDescription`, `pid`, `tstamp`, `crdate`, `deleted`, `hidden`, `starttime`, `endtime`, `fe_group`, `sorting`, `editlock`, `sys_language_uid`, `l18n_parent`, `l10n_source`, `l10n_state`, `l18n_diffsource`, `t3ver_oid`, `t3ver_wsid`, `t3ver_state`, `t3ver_stage`, `CType`, `header`, `header_position`, `bodytext`, `bullets_type`, `uploads_description`, `uploads_type`, `assets`, `image`, `imagewidth`, `imageorient`, `imagecols`, `imageborder`, `media`, `layout`, `frame_class`, `cols`, `space_before_class`, `space_after_class`, `records`, `pages`, `colPos`, `subheader`, `header_link`, `image_zoom`, `header_layout`, `list_type`, `sectionIndex`, `linkToTop`, `file_collections`, `filelink_size`, `filelink_sorting`, `filelink_sorting_direction`, `target`, `recursive`, `imageheight`, `pi_flexform`, `category_field`, `table_class`, `table_caption`, `table_delimiter`, `table_enclosure`, `table_header_position`, `table_tfoot`, `categories`, `selected_categories`, `date`, `tx_impexp_origuid`, `tx_ximatypo3contentplanner_status`, `tx_ximatypo3contentplanner_assignee`, `tx_ximatypo3contentplanner_comments`) +VALUES + ('1', '', '1', '1736359280', '1736359280', '0', '0', '0', '0', '', '256', '0', '0', '0', '0', '0', X'', '0', '0', '0', '0', 'text', 'Welcome Home', '', '

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

', '0', '0', '0', '0', '0', '0', '0', '2', '0', '0', '0', 'default', '0', '', '', NULL, NULL, '0', '', '', '0', '0', '', '1', '0', NULL, '0', '', '', '', '0', '0', NULL, '', '', NULL, '124', '0', '0', '0', '0', NULL, '0', '0', '0', '0', '0'), + ('2', '', '1', '1736359378', '1736359378', '0', '0', '0', '0', '', '512', '0', '0', '0', '0', '0', X'', '0', '0', '0', '0', 'bullets', 'Features', '', 'First point\r\nSecond point\r\nThird point', '0', '0', '0', '0', '0', '0', '0', '2', '0', '0', '0', 'default', '0', '', '', NULL, NULL, '0', '', '', '0', '0', '', '1', '0', NULL, '0', '', '', '', '0', '0', NULL, '', '', NULL, '124', '0', '0', '0', '0', NULL, '0', '0', '0', '0', '0'); + +# sys_category + +INSERT INTO `sys_category` (`uid`, `pid`, `tstamp`, `crdate`, `deleted`, `hidden`, `starttime`, `endtime`, `sorting`, `description`, `sys_language_uid`, `l10n_parent`, `l10n_state`, `l10n_diffsource`, `t3ver_oid`, `t3ver_wsid`, `t3ver_state`, `t3ver_stage`, `title`, `items`, `parent`, `tx_ximatypo3contentplanner_status`, `tx_ximatypo3contentplanner_assignee`, `tx_ximatypo3contentplanner_comments`) +VALUES + ('1', '6', '1736359306', '1736359306', '0', '0', '0', '0', '256', '', '0', '0', '0', X'', '0', '0', '0', '0', 'Important', '0', '0', '0', '0', '0'), + ('2', '6', '1736359322', '1736359322', '0', '0', '0', '0', '512', '', '0', '0', '0', X'', '0', '0', '0', '0', 'Seasonal', '0', '0', '0', '0', '0'), + ('3', '6', '1736359338', '1736359338', '0', '0', '0', '0', '768', '', '0', '0', '0', X'', '0', '0', '0', '0', 'Sales', '0', '0', '0', '0', '0'); diff --git a/.ddev/test/packages/sitepackage/Classes/EventListener/PrepareStatusSelectionListener.php b/.ddev/test/packages/sitepackage/Classes/EventListener/PrepareStatusSelectionListener.php new file mode 100644 index 0000000..af28f8e --- /dev/null +++ b/.ddev/test/packages/sitepackage/Classes/EventListener/PrepareStatusSelectionListener.php @@ -0,0 +1,31 @@ +getSelection(); + + if ($event->getCurrentStatus() && $event->getCurrentStatus()->getTitle() === 'Needs review') { + $targetStatus = $this->statusRepository->findOneByTitle('Pending'); + + if ($targetStatus) { + unset($selection[$targetStatus->getUid()]); + } + } + + $event->setSelection($selection); + } +} diff --git a/.ddev/test/packages/sitepackage/Configuration/Services.yaml b/.ddev/test/packages/sitepackage/Configuration/Services.yaml new file mode 100644 index 0000000..cb1fea6 --- /dev/null +++ b/.ddev/test/packages/sitepackage/Configuration/Services.yaml @@ -0,0 +1,14 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Test\Sitepackage\: + resource: '../Classes/*' + exclude: '../Classes/Domain/Model/*' + + Test\Sitepackage\EventListener\PrepareStatusSelectionListener: + tags: + - name: event.listener + identifier: 'xima-typo3-content-planner/prepare-status-selection' \ No newline at end of file diff --git a/.ddev/test/packages/sitepackage/Configuration/TCA/Overrides/sys_category.php b/.ddev/test/packages/sitepackage/Configuration/TCA/Overrides/sys_category.php new file mode 100644 index 0000000..bf01200 --- /dev/null +++ b/.ddev/test/packages/sitepackage/Configuration/TCA/Overrides/sys_category.php @@ -0,0 +1,7 @@ +table === 'pages' || in_array($this->table, $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['registerAdditionalRecordTables']); + return ExtensionUtility::isRegisteredRecordTable($this->table) && $this->identifier; } public function getPriority(): int @@ -40,6 +52,8 @@ protected function getAdditionalAttributes(string|int $itemName): array return [ 'data-callback-module' => '@xima/ximatypo3contentplanner/context-menu-actions', 'data-status' => $itemName, + 'data-uri' => UrlHelper::getContentStatusPropertiesEditUrl($this->table, (int)$this->identifier, false), + 'data-new-comment-uri' => UrlHelper::getNewCommentUrl($this->table, (int)$this->identifier), ]; } @@ -48,23 +62,71 @@ public function addItems(array $items): array if (!VisibilityUtility::checkContentStatusVisibility()) { return $items; } + /** @var PageRenderer $pageRenderer */ + $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); + $pageRenderer->loadJavaScriptModule('@xima/ximatypo3contentplanner/new-comment-modal.js'); + $pageRenderer->loadJavaScriptModule('@xima/ximatypo3contentplanner/comments-modal.js'); + $pageRenderer->addInlineLanguageLabelFile('EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf'); $this->initDisabledItems(); + $itemsToAdd = []; foreach ($this->statusRepository->findAll() as $statusItem) { - $this->itemsConfiguration['wrap']['childItems'][$statusItem->getUid()] = [ + $itemsToAdd[$statusItem->getUid()] = [ 'label' => $statusItem->getTitle(), 'iconIdentifier' => $statusItem->getColoredIcon(), 'callbackAction' => 'change', ]; } - $this->itemsConfiguration['wrap']['childItems']['divider'] = ['type' => 'divider']; + $itemsToAdd['divider'] = ['type' => 'divider']; - $this->itemsConfiguration['wrap']['childItems']['reset'] = [ + $itemsToAdd['reset'] = [ 'label' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_be.xlf:reset', 'iconIdentifier' => 'actions-close', 'callbackAction' => 'reset', ]; + $record = null; + if (ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_EXTEND_CONTEXT_MENU)) { + $record = $this->recordRepository->findByUid($this->table, (int)$this->identifier); + if ($record) { + $itemsToAdd['divider2'] = ['type' => 'divider']; + + // remove current status from list + if (in_array($record['tx_ximatypo3contentplanner_status'], array_keys($itemsToAdd), true)) { + unset($itemsToAdd[$record['tx_ximatypo3contentplanner_status']]); + } + + // remove reset if status is already null + if ($record['tx_ximatypo3contentplanner_status'] === null) { + unset($itemsToAdd['reset']); + } + + // assignee + if ($record['tx_ximatypo3contentplanner_assignee']) { + $username = $this->backendUserRepository->getUsernameByUid($record['tx_ximatypo3contentplanner_assignee']); + $itemsToAdd['assignee'] = [ + 'label' => $username, + 'iconIdentifier' => 'actions-user', + 'callbackAction' => 'load', + ]; + } + + // comments + if ($record['tx_ximatypo3contentplanner_status'] !== null) { + $itemsToAdd['comments'] = [ + 'label' => $this->getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_be.xlf:comments') . ($record['tx_ximatypo3contentplanner_comments'] ? ' (' . $record['tx_ximatypo3contentplanner_comments'] . ')' : ''), + 'iconIdentifier' => 'actions-message', + 'callbackAction' => 'comments', + ]; + } + } + } + + $this->statusSelectionManager->prepareStatusSelection($this, $this->table, (int)$this->identifier, $itemsToAdd, $record ? $record['tx_ximatypo3contentplanner_status'] : null); + foreach ($itemsToAdd as $itemKey => $itemToAdd) { + $this->itemsConfiguration['wrap']['childItems'][$itemKey] = $itemToAdd; + } + $localItems = $this->prepareItems($this->itemsConfiguration); if (isset($items['info'])) { @@ -86,4 +148,9 @@ protected function canRender(string|int $itemName, string $type): bool } return true; } + + protected function getLanguageService(): LanguageService + { + return $GLOBALS['LANG']; + } } diff --git a/Classes/Backend/ToolbarItems/UpdateItem.php b/Classes/Backend/ToolbarItems/UpdateItem.php deleted file mode 100644 index d466aa0..0000000 --- a/Classes/Backend/ToolbarItems/UpdateItem.php +++ /dev/null @@ -1,116 +0,0 @@ -setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:' . Configuration::EXT_KEY - . '/Resources/Private/Templates/Backend/ToolbarItems/UpdateItem.html')); - return $view->assignMultiple([ - 'count' => count($this->getRelevantUpdates()), - ])->render(); - } - - /** - * TRUE if this toolbar item has a collapsible drop down - * - * @return bool - */ - public function hasDropDown(): bool - { - return true; - } - - /** - * Render "drop down" part of this toolbar - * - * @return string Drop down HTML - */ - public function getDropDown(): string - { - $view = GeneralUtility::makeInstance(StandaloneView::class); - $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:' . Configuration::EXT_KEY - . '/Resources/Private/Templates/Backend/ToolbarItems/UpdateItemDropDown.html')); - $view->setPartialRootPaths([ - GeneralUtility::getFileAbsFileName('EXT:' . Configuration::EXT_KEY . '/Resources/Private/Partials/'), - ]); - return $view->assignMultiple([ - 'data' => $this->getRelevantUpdates(), - ])->render(); - } - - /** - * Returns an array with additional attributes added to containing
  • tag of the item. - * - * Typical usages are additional css classes and data-* attributes, classes may be merged - * with other classes needed by the framework. Do NOT set an id attribute here. - * - * array( - * 'class' => 'my-class', - * 'data-foo' => '42', - * ) - * - * @return array List item HTML attributes - */ - public function getAdditionalAttributes(): array - { - return []; - } - - /** - * Returns an integer between 0 and 100 to determine - * the position of this item relative to others - * - * By default, extensions should return 50 to be sorted between main core - * items and other items that should be on the very right. - * - * @return int 0 .. 100 - */ - public function getIndex(): int - { - return 50; - } - - private function getRelevantUpdates(): array - { - $cacheIdentifier = sha1('contentplanner_toolbarcache' . $GLOBALS['BE_USER']->user['uid']); - $data = $this->cache->get($cacheIdentifier); - if ($data === false) { - // Store the data in cache - $data = $this->contentUpdateDataProvider->fetchUpdateData(true, maxItems: 5); - $this->cache->set($cacheIdentifier, $data, ['ximatypo3contentplanner_toolbarcache'], 300); - } - - return $data ?: []; - } -} diff --git a/Classes/Command/BulkUpdateCommand.php b/Classes/Command/BulkUpdateCommand.php index 2b1f333..390445c 100644 --- a/Classes/Command/BulkUpdateCommand.php +++ b/Classes/Command/BulkUpdateCommand.php @@ -9,13 +9,21 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Domain\Repository\PageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Configuration; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\CommentRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\RecordRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; +use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; final class BulkUpdateCommand extends Command { + public function __construct(private readonly StatusRepository $statusRepository, private readonly RecordRepository $recordRepository, private readonly CommentRepository $commentRepository) + { + parent::__construct(); + } + protected function configure(): void { $this @@ -24,6 +32,7 @@ protected function configure(): void ->addArgument('uid', InputArgument::OPTIONAL, 'The uid to update.', 1) ->addArgument('status', InputArgument::OPTIONAL, 'The status uid to set. If empty, the status will be cleared.', null) ->addOption('recursive', 'r', InputOption::VALUE_OPTIONAL, 'Whether to update pages recursively.', false) + ->addOption('assignee', 'a', InputOption::VALUE_REQUIRED, 'The backend user uid to set an assignee for this record.', null) ->addUsage('pages 1 4') ->setHelp('A command to perform a bulk operation to content planner entities.'); } @@ -34,17 +43,23 @@ protected function execute(InputInterface $input, OutputInterface $output): int $uid = (int)$input->getArgument('uid'); $status = (int)$input->getArgument('status'); $recursive = $input->getOption('recursive') !== false; + $assignee = $input->getOption('assignee'); $statusEntity = null; if ($status === 0) { $status = null; } else { - $statusEntity = ContentUtility::getStatus($status); + $statusEntity = $this->statusRepository->findByUid($status); if ($statusEntity === null) { $output->writeln(sprintf('Status with uid %d not found.', $status)); return Command::FAILURE; } } + if ($assignee !== null) { + if ($assignee === 0) { + $assignee = null; + } + } $count = 0; $uids = [$uid]; @@ -54,14 +69,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int } foreach ($uids as $uid) { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); - $queryBuilder - ->update($table) - ->set('tx_ximatypo3contentplanner_status', $status) - ->where( - $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) - ) - ->executeStatement(); + $this->recordRepository->updateStatusByUid($table, $uid, $status, $assignee); + + if ($status === null && ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_CLEAR_COMMENTS_ON_STATUS_RESET)) { + $this->commentRepository->deleteAllCommentsByRecord($uid, $table); + } $count++; } diff --git a/Classes/Command/NotifyUpdateCommand.php b/Classes/Command/NotifyUpdateCommand.php deleted file mode 100644 index a7720ce..0000000 --- a/Classes/Command/NotifyUpdateCommand.php +++ /dev/null @@ -1,102 +0,0 @@ - 86400, - 'weekly' => 604800, - ]; - - public function __construct(private readonly ContentUpdateDataProvider $contentUpdateDataProvider) - { - parent::__construct(); - } - - protected function configure(): void - { - $this - ->setHelp('A command to notify users about relevant updates in the content planner.'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $backendUserRepository = GeneralUtility::makeInstance(BackendUserRepository::class); - - // ToDo: only regard users with access to the content planner - foreach ($backendUserRepository->findAll() as $user) { - if ($user->getEmail() === '' || !(bool)$user->getSubscribe() || ($user->getLastMail() + self::SUBSCRIBE_FREQUENCY[$user->getSubscribe()]) > time()) { - continue; - } - $output->writeln('Checking updates for user ' . $user->getUsername() . ' ...'); - - $since = time() - (int)self::SUBSCRIBE_FREQUENCY[$user->getSubscribe()]; - - $updates = $this->contentUpdateDataProvider->fetchUpdateData(false, null, $since, null, true); - if (count($updates) > 0) { - $output->writeln('User ' . $user->getUsername() . ' has ' . count($updates) . ' updates.'); - $result = $this->notify($user, $updates); - - if ($result) { - $user->setLastMail(time()); - $backendUserRepository->update($user); - // ToDo: why isn't the user updated? - } - } - } - - return Command::SUCCESS; - } - - private function notify(BackendUser $user, array $updates): bool - { - $siteFinder = GeneralUtility::makeInstance(SiteFinder::class); - $array = $siteFinder->getAllSites(); - $site = reset($array); - - $view = GeneralUtility::makeInstance(StandaloneView::class); - /** @var StandaloneView $view */ - $view->setTemplatePathAndFilename('EXT:' . Configuration::EXT_KEY . '/Resources/Private/Templates/Email/ContentUpdates.html'); - - $view->setTemplateRootPaths(['EXT:' . Configuration::EXT_KEY . '/Resources/Private/Templates/']); - $view->setPartialRootPaths(['EXT:' . Configuration::EXT_KEY . '/Resources/Private/Partials/']); - $view->setLayoutRootPaths(['EXT:' . Configuration::EXT_KEY . '/Resources/Private/Layouts/']); - - $view->assignMultiple( - [ - 'data' => $updates, - 'user' => $user, - 'backendUrl' => $site->getBase()->__toString() . '/typo3', - ] - ); - - $email = new MailMessage(); - $email - ->to(new Address($user->getEmail(), $user->getUsername())) - ->subject('TYPO3 Content Planner Updates') - ->html( - // workaround: to resolve the base url in the email - str_replace('href="', 'href="' . $site->getBase()->__toString() . '/', $view->render()) - ); - GeneralUtility::makeInstance(MailerInterface::class)->send($email); - - return true; - } -} diff --git a/Classes/Configuration.php b/Classes/Configuration.php index 6c34543..39dbb09 100644 --- a/Classes/Configuration.php +++ b/Classes/Configuration.php @@ -9,48 +9,12 @@ class Configuration final public const EXT_KEY = 'xima_typo3_content_planner'; final public const EXT_NAME = 'XimaTypo3ContentPlanner'; - final public const STATUS_ICON_FLAG = 'flag'; - final public const STATUS_ICON_HEART = 'heart'; - final public const STATUS_ICON_TAG = 'tag'; - final public const STATUS_ICON_STAR = 'star'; - final public const STATUS_ICON_INFO = 'info'; - - final public const STATUS_ICONS = [ - self::STATUS_ICON_FLAG, - self::STATUS_ICON_HEART, - self::STATUS_ICON_TAG, - self::STATUS_ICON_STAR, - self::STATUS_ICON_INFO, - - ]; - - final public const STATUS_COLOR_BLACK = 'black'; - final public const STATUS_COLOR_BLUE = 'blue'; - final public const STATUS_COLOR_GREEN = 'green'; - final public const STATUS_COLOR_YELLOW = 'yellow'; - final public const STATUS_COLOR_RED = 'red'; - final public const STATUS_COLOR_GRAY = 'gray'; - - final public const STATUS_COLORS = [ - self::STATUS_COLOR_BLACK, - self::STATUS_COLOR_BLUE, - self::STATUS_COLOR_GREEN, - self::STATUS_COLOR_YELLOW, - self::STATUS_COLOR_RED, - self::STATUS_COLOR_GRAY, - ]; - - final public const STATUS_COLOR_CODES = [ - self::STATUS_COLOR_RED => '#f8d7da', - self::STATUS_COLOR_BLUE => '#cce5ff', - self::STATUS_COLOR_YELLOW => '#fff3cd', - self::STATUS_COLOR_GREEN => '#d4edda', - ]; - - final public const STATUS_COLOR_ALERTS = [ - self::STATUS_COLOR_RED => 'danger', - self::STATUS_COLOR_BLUE => 'info', - self::STATUS_COLOR_YELLOW => 'warning', - self::STATUS_COLOR_GREEN => 'success', - ]; + final public const FEATURE_AUTO_ASSIGN = 'autoAssignment'; + final public const FEATURE_EXTEND_CONTEXT_MENU = 'extendedContextMenu'; + final public const FEATURE_CURRENT_ASSIGNEE_HIGHLIGHT = 'currentAssigneeHighlight'; + final public const FEATURE_CLEAR_COMMENTS_ON_STATUS_RESET = 'clearCommentsOnStatusReset'; + final public const FEATURE_RECORD_EDIT_HEADER_INFO = 'recordEditHeaderInfo'; + final public const FEATURE_RESET_CONTENT_ELEMENT_STATUS_ON_PAGE_RESET = 'resetContentElementStatusOnPageReset'; + + final public const CACHE_IDENTIFIER = 'ximatypo3contentplanner'; } diff --git a/Classes/Configuration/Colors.php b/Classes/Configuration/Colors.php new file mode 100644 index 0000000..a6cb3eb --- /dev/null +++ b/Classes/Configuration/Colors.php @@ -0,0 +1,50 @@ + '144,164,174', + self::STATUS_COLOR_RED => '250,136,147', + self::STATUS_COLOR_BLUE => '100,187,200', + self::STATUS_COLOR_YELLOW => '255,205,117', + self::STATUS_COLOR_GREEN => '106,158,113', + self::STATUS_COLOR_PURPLE => '92,107,192', + self::STATUS_COLOR_ORANGE => '255,112,67', + ]; + + public static function get(string $colorCode, bool $transparency = false): string + { + if (!in_array($colorCode, self::STATUS_COLORS)) { + throw new \InvalidArgumentException('Invalid color code', 2653877737); + } + + if (!array_key_exists($colorCode, self::COLOR_CODES)) { + return $colorCode; + } + + $key = $transparency ? 'rgba' : 'rgb'; + return $key . '(' . self::COLOR_CODES[$colorCode] . ($transparency ? ', 0.5' : '') . ')'; + } +} diff --git a/Classes/Configuration/Icons.php b/Classes/Configuration/Icons.php new file mode 100644 index 0000000..cb3523f --- /dev/null +++ b/Classes/Configuration/Icons.php @@ -0,0 +1,30 @@ +getQueryParams()) ? $request->getQueryParams()['search'] : null; @@ -23,7 +28,7 @@ public function filterAction(ServerRequestInterface $request): ResponseInterface $assignee = array_key_exists('assignee', $request->getQueryParams()) ? (int)$request->getQueryParams()['assignee'] : null; $type = array_key_exists('type', $request->getQueryParams()) ? $request->getQueryParams()['type'] : null; - $records = ContentUtility::getRecordsByFilter($search, $status, $assignee, $type); + $records = $this->recordRepository->findAllByFilter($search, $status, $assignee, $type); $result = []; foreach ($records as $record) { $result[] = StatusItem::create($record)->toArray(); @@ -35,7 +40,7 @@ public function commentsAction(ServerRequestInterface $request): ResponseInterfa { $recordId = (int)$request->getQueryParams()['uid']; $recordTable = $request->getQueryParams()['table']; - $comments = ContentUtility::getComments($recordId, $recordTable); + $comments = $this->commentRepository->findAllByRecord($recordId, $recordTable); /** @var StandaloneView $view */ $view = GeneralUtility::makeInstance(StandaloneView::class); diff --git a/Classes/Controller/TreeController.php b/Classes/Controller/TreeController.php index 1123761..ee77f80 100644 --- a/Classes/Controller/TreeController.php +++ b/Classes/Controller/TreeController.php @@ -29,7 +29,7 @@ protected function initializePageTreeRepository(): PageTreeRepository $pageTreeRepository = GeneralUtility::makeInstance( PageTreeRepository::class, $backendUser->workspace, - ['subtitle', 'tx_ximatypo3contentplanner_status'], + ['tx_ximatypo3contentplanner_status'], $additionalQueryRestrictions ); $pageTreeRepository->setAdditionalWhereClause($backendUser->getPagePermsClause(Permission::PAGE_SHOW)); diff --git a/Classes/Domain/Model/Dto/CommentItem.php b/Classes/Domain/Model/Dto/CommentItem.php index 71ab00c..fb9eb1a 100644 --- a/Classes/Domain/Model/Dto/CommentItem.php +++ b/Classes/Domain/Model/Dto/CommentItem.php @@ -2,14 +2,12 @@ namespace Xima\XimaTypo3ContentPlanner\Domain\Model\Dto; -use TYPO3\CMS\Backend\Routing\UriBuilder; -use TYPO3\CMS\Core\Imaging\Icon; -use TYPO3\CMS\Core\Imaging\IconFactory; -use TYPO3\CMS\Core\Localization\LanguageService; -use TYPO3\CMS\Core\Utility\GeneralUtility; use Xima\XimaTypo3ContentPlanner\Domain\Model\Status; use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; +use Xima\XimaTypo3ContentPlanner\Utility\IconHelper; use Xima\XimaTypo3ContentPlanner\Utility\PermissionUtility; +use Xima\XimaTypo3ContentPlanner\Utility\UrlHelper; final class CommentItem { @@ -27,8 +25,7 @@ public static function create(array $row): static public function getTitle(): string { - $titleField = $GLOBALS['TCA'][$this->data['foreign_table']]['ctrl']['label']; - return $this->getRelatedRecord() ? (array_key_exists($titleField, $this->getRelatedRecord()) ? $this->getRelatedRecord()[$titleField] : $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title')) : $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title'); + return ExtensionUtility::getTitle(ExtensionUtility::getTitleField($this->data['foreign_table']), $this->getRelatedRecord()); } public function getRelatedRecord(): array|bool @@ -46,38 +43,21 @@ public function getRelatedRecord(): array|bool public function getStatusIcon(): string { - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - $status = ContentUtility::getStatus($this->getRelatedRecord()['tx_ximatypo3contentplanner_status']); - $icon = $iconFactory->getIcon($status ? $status->getColoredIcon() : 'flag-gray', 'small'); - return $icon->getIdentifier(); + return IconHelper::getIconByStatusUid((int)$this->getRelatedRecord()['tx_ximatypo3contentplanner_status']); } public function getRecordIcon(): string { - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - if (!$this->getRelatedRecord()) { - return ''; - } - return $iconFactory->getIconForRecord($this->data['foreign_table'], $this->getRelatedRecord(), Icon::SIZE_SMALL)->getIdentifier(); + return IconHelper::getIconByRecord($this->data['foreign_table'], $this->getRelatedRecord()); } public function getRecordLink(): string { - switch ($this->data['foreign_table']) { - case 'pages': - return (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('web_layout', ['id' => $this->data['foreign_uid']]); - default: - return (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('record_edit', ['edit' => [$this->data['foreign_table'] => [$this->data['foreign_uid'] => 'edit']]]); - } + return UrlHelper::getRecordLink($this->data['foreign_table'], (int)$this->data['foreign_uid']); } public function getAuthorName(): ?string { return ContentUtility::getBackendUsernameById((int)$this->data['author']); } - - protected function getLanguageService(): LanguageService - { - return $GLOBALS['LANG']; - } } diff --git a/Classes/Domain/Model/Dto/HistoryItem.php b/Classes/Domain/Model/Dto/HistoryItem.php index b7de803..67606e2 100644 --- a/Classes/Domain/Model/Dto/HistoryItem.php +++ b/Classes/Domain/Model/Dto/HistoryItem.php @@ -2,37 +2,48 @@ namespace Xima\XimaTypo3ContentPlanner\Domain\Model\Dto; -use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore; -use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Localization\LanguageService; use TYPO3\CMS\Core\Utility\GeneralUtility; +use Xima\XimaTypo3ContentPlanner\Configuration; use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; use Xima\XimaTypo3ContentPlanner\Utility\DiffUtility; use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; +use Xima\XimaTypo3ContentPlanner\Utility\IconHelper; use Xima\XimaTypo3ContentPlanner\Utility\PermissionUtility; +use Xima\XimaTypo3ContentPlanner\Utility\UrlHelper; final class HistoryItem { public array $data = []; - public array|bool $relatedRecord = []; - public bool $cliContext = false; + public array|bool|null $relatedRecord = []; - public static function create(array $sysHistoryRow, bool $cliContext = false): static + public static function create(array $sysHistoryRow): static { $item = new HistoryItem(); $item->data = $sysHistoryRow; $item->data['raw_history'] = json_decode($sysHistoryRow['history_data'], true); - $item->cliContext = $cliContext; return $item; } public function getAssignedToCurrentUser(): bool { + if (!ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_CURRENT_ASSIGNEE_HIGHLIGHT)) { + return false; + } + $record = ContentUtility::getExtensionRecord($this->data['tablename'], (int)$this->data['recuid']); + if ($record === null) { + return false; + } + + if ($this->data['tablename'] === 'tx_ximatypo3contentplanner_comment') { + $record = ContentUtility::getExtensionRecord($this->data['raw_history']['foreign_table'], (int)$this->data['raw_history']['foreign_uid']); + } + if ($record === null || !array_key_exists('tx_ximatypo3contentplanner_assignee', $record)) { return false; } @@ -46,8 +57,7 @@ public function getPid(): int public function getTitle(): ?string { - $titleField = $GLOBALS['TCA'][$this->data['relatedRecordTablename']]['ctrl']['label']; - return array_key_exists($titleField, $this->getRelatedRecord()) ? $this->getRelatedRecord()[$titleField] : $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title'); + return ExtensionUtility::getTitle(ExtensionUtility::getTitleField($this->data['relatedRecordTablename']), $this->getRelatedRecord()); } public function getRelatedRecord(): array|bool @@ -59,7 +69,12 @@ public function getRelatedRecord(): array|bool $this->relatedRecord = ContentUtility::getPage((int)$this->data['recuid']); break; case 'tx_ximatypo3contentplanner_comment': - if ($this->data['raw_history']['foreign_table'] && $this->data['raw_history']['foreign_uid']) { + if ( + array_key_exists('foreign_table', $this->data['raw_history']) + && array_key_exists('foreign_uid', $this->data['raw_history']) + && $this->data['raw_history']['foreign_table'] + && $this->data['raw_history']['foreign_uid'] + ) { $table = $this->data['raw_history']['foreign_table']; $uid = (int)$this->data['raw_history']['foreign_uid']; } else { @@ -85,13 +100,7 @@ public function getRelatedRecord(): array|bool public function getRecordLink(): string { - $record = $this->getRelatedRecord(); - switch ($this->data['relatedRecordTablename']) { - case 'pages': - return (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('web_layout', ['id' => $record['uid']]); - default: - return (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('record_edit', ['edit' => [$this->data['relatedRecordTablename'] => [$record['uid'] => 'edit']]]); - } + return UrlHelper::getRecordLink($this->data['relatedRecordTablename'], (int)$this->getRelatedRecord()['uid']); } public function getStatus(): ?string @@ -102,16 +111,12 @@ public function getStatus(): ?string public function getStatusIcon(): ?string { - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - $status = ContentUtility::getStatus($this->getRelatedRecord()['tx_ximatypo3contentplanner_status']); - return $this->renderIcon($iconFactory->getIcon($status ? $status->getColoredIcon() : 'flag-gray', Icon::SIZE_SMALL)); + return IconHelper::getIconByStatusUid((int)$this->getRelatedRecord()['tx_ximatypo3contentplanner_status'], true); } public function getRecordIcon(): string { - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - $record = $this->getRelatedRecord(); - return $record ? $this->renderIcon($iconFactory->getIconForRecord($this->data['relatedRecordTablename'], $record, Icon::SIZE_SMALL)) : ''; + return IconHelper::getIconByRecord($this->data['relatedRecordTablename'], $this->getRelatedRecord(), true); } public function getTimeAgo(): string @@ -129,24 +134,20 @@ public function getChangeTypeIcon(): string $iconFactory = GeneralUtility::makeInstance(IconFactory::class); switch ($this->data['tablename']) { case 'tx_ximatypo3contentplanner_comment': - return $this->renderIcon($iconFactory->getIcon('actions-comment', Icon::SIZE_SMALL)); + return IconHelper::getIconByIdentifier('actions-comment'); default: if (!ExtensionUtility::isRegisteredRecordTable($this->data['tablename'])) { break; } switch (array_key_first($this->data['raw_history']['newRecord'])) { case 'tx_ximatypo3contentplanner_status': - $status = ContentUtility::getStatus((int)$this->data['raw_history']['newRecord']['tx_ximatypo3contentplanner_status']); - if (!$status) { - return $this->renderIcon($iconFactory->getIcon('flag-gray', Icon::SIZE_SMALL)); - } - return $this->renderIcon($iconFactory->getIcon($status->getColoredIcon(), Icon::SIZE_SMALL)); + return IconHelper::getIconByStatusUid((int)$this->data['raw_history']['newRecord']['tx_ximatypo3contentplanner_status'], true); case 'tx_ximatypo3contentplanner_assignee': - return $this->renderIcon($iconFactory->getIcon('actions-user', Icon::SIZE_SMALL)); + return IconHelper::getIconByIdentifier('actions-user'); } break; } - return $this->renderIcon($iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)); + return IconHelper::getIconByIdentifier('actions-open'); } public function getRawHistoryData(): array @@ -178,9 +179,4 @@ protected function getLanguageService(): LanguageService { return $GLOBALS['LANG']; } - - protected function renderIcon(Icon $icon): string - { - return $this->cliContext ? $icon->getAlternativeMarkup('inline') : $icon->render(); - } } diff --git a/Classes/Domain/Model/Dto/StatusItem.php b/Classes/Domain/Model/Dto/StatusItem.php index f24ab24..7b128ab 100644 --- a/Classes/Domain/Model/Dto/StatusItem.php +++ b/Classes/Domain/Model/Dto/StatusItem.php @@ -2,15 +2,15 @@ namespace Xima\XimaTypo3ContentPlanner\Domain\Model\Dto; -use TYPO3\CMS\Backend\Backend\Avatar\Avatar; -use TYPO3\CMS\Backend\Routing\UriBuilder; -use TYPO3\CMS\Backend\Utility\BackendUtility; -use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; +use Xima\XimaTypo3ContentPlanner\Configuration; use Xima\XimaTypo3ContentPlanner\Domain\Model\Status; use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; +use Xima\XimaTypo3ContentPlanner\Utility\IconHelper; +use Xima\XimaTypo3ContentPlanner\Utility\UrlHelper; final class StatusItem { @@ -28,12 +28,15 @@ public static function create(array $row): static public function getAssignedToCurrentUser(): bool { + if (!ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_CURRENT_ASSIGNEE_HIGHLIGHT)) { + return false; + } return ((int)$this->data['tx_ximatypo3contentplanner_assignee']) === $GLOBALS['BE_USER']->user['uid']; } public function getTitle(): ?string { - return $this->data['title'] !== '' ? $this->data['title'] : BackendUtility::getNoRecordTitle(); + return ExtensionUtility::getTitle('title', $this->data); } public function getStatus(): ?string @@ -43,25 +46,17 @@ public function getStatus(): ?string public function getStatusIcon(): string { - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - $icon = $iconFactory->getIcon($this->status ? $this->status->getColoredIcon() : 'flag-gray', 'small'); - return $icon->render(); + return IconHelper::getIconByStatus($this->status, true); } public function getRecordIcon(): string { - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - return $iconFactory->getIconForRecord($this->data['tablename'], $this->data, Icon::SIZE_SMALL)->render(); + return IconHelper::getIconByRecord($this->data['tablename'], $this->data, true); } public function getRecordLink(): string { - switch ($this->data['tablename']) { - case 'pages': - return (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('web_layout', ['id' => $this->data['uid']]); - default: - return (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('record_edit', ['edit' => [$this->data['tablename'] => [$this->data['uid'] => 'edit']]]); - } + return UrlHelper::getRecordLink($this->data['tablename'], $this->data['uid']); } public function getAssignee(): ?string @@ -76,19 +71,12 @@ public function getAssigneeName(): ?string public function getAssigneeAvatar(): ?string { - $backendUser = ContentUtility::getBackendUserById((int)$this->data['tx_ximatypo3contentplanner_assignee']); - if (!$backendUser) { - return null; - } - $avatar = GeneralUtility::makeInstance(Avatar::class); - return $avatar->render($backendUser, 15, true); + return IconHelper::getAvatarByUserId((int)$this->data['tx_ximatypo3contentplanner_assignee']); } public function getComments(): ?string { - $iconFactory = GeneralUtility::makeInstance(IconFactory::class); - $icon = $iconFactory->getIcon('content-message', 'small'); - return $this->data['tx_ximatypo3contentplanner_comments'] . ' ' . $icon->render(); + return $this->data['tx_ximatypo3contentplanner_comments'] . ' ' . IconHelper::getIconByIdentifier('actions-message'); } public function getSite(): ?string diff --git a/Classes/Domain/Repository/BackendUserRepository.php b/Classes/Domain/Repository/BackendUserRepository.php index 40d394f..e3c590a 100644 --- a/Classes/Domain/Repository/BackendUserRepository.php +++ b/Classes/Domain/Repository/BackendUserRepository.php @@ -4,8 +4,64 @@ namespace Xima\XimaTypo3ContentPlanner\Domain\Repository; -use TYPO3\CMS\Extbase\Persistence\Repository; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Utility\GeneralUtility; -class BackendUserRepository extends Repository +class BackendUserRepository { + /** + * @throws \Doctrine\DBAL\Exception + */ + public function findAll(): array|bool + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); + + return $queryBuilder + ->select('*') + ->from('be_users') + ->executeQuery()->fetchAllAssociative(); + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function findByUid(int $uid): array|bool + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); + + return $queryBuilder + ->select('*') + ->from('be_users') + ->where( + $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) + ) + ->executeQuery()->fetchAssociative(); + } + + /** + * @ToDo: Check if there is a core function to get the username by uid + * @throws \Doctrine\DBAL\Exception + */ + public function getUsernameByUid(int $uid): string + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); + + $userRecord = $queryBuilder + ->select('username', 'realName') + ->from('be_users') + ->where( + $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) + ) + ->executeQuery()->fetchAssociative(); + + if ($userRecord) { + $user = $userRecord['username']; + if ($userRecord['realName']) { + $user = $userRecord['realName'] . ' (' . $user . ')'; + } + return htmlspecialchars($user, ENT_QUOTES, 'UTF-8'); + } + + return ''; + } } diff --git a/Classes/Domain/Repository/CommentRepository.php b/Classes/Domain/Repository/CommentRepository.php new file mode 100644 index 0000000..efe2285 --- /dev/null +++ b/Classes/Domain/Repository/CommentRepository.php @@ -0,0 +1,91 @@ + QueryInterface::ORDER_DESCENDING, + ]; + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function findAllByRecord(int $id, string $table): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE); + + $comments = $queryBuilder + ->select('*') + ->from(self::TABLE) + ->where( + $queryBuilder->expr()->eq('foreign_uid', $queryBuilder->createNamedParameter($id, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)), + $queryBuilder->expr()->eq('foreign_table', $queryBuilder->createNamedParameter($table, \TYPO3\CMS\Core\Database\Connection::PARAM_STR)), + $queryBuilder->expr()->eq('deleted', 0) + ) + ->orderBy('tstamp', 'DESC') + ->executeQuery()->fetchAllAssociative(); + + $items = []; + foreach ($comments as $result) { + try { + $items[] = CommentItem::create($result); + } catch (\Exception $e) { + } + } + + return $items; + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function findByUid(int $uid): array|bool + { + if (!$uid) { + return false; + } + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE); + + return $queryBuilder + ->select('*') + ->from(self::TABLE) + ->where( + $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)), + $queryBuilder->expr()->eq('deleted', 0) + ) + ->executeQuery()->fetchAssociative(); + } + + public function deleteAllCommentsByRecord(int $id, string $table): void + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE); + $queryBuilder + ->update(self::TABLE) + ->set('deleted', 1) + ->where( + $queryBuilder->expr()->eq('foreign_uid', $queryBuilder->createNamedParameter($id, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)), + $queryBuilder->expr()->eq('foreign_table', $queryBuilder->createNamedParameter($table, \TYPO3\CMS\Core\Database\Connection::PARAM_STR)) + ) + ->executeStatement(); + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder + ->update($table) + ->set('tx_ximatypo3contentplanner_comments', 0) + ->where( + $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($id, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)), + ) + ->executeStatement(); + } +} diff --git a/Classes/Domain/Repository/RecordRepository.php b/Classes/Domain/Repository/RecordRepository.php new file mode 100644 index 0000000..44b481e --- /dev/null +++ b/Classes/Domain/Repository/RecordRepository.php @@ -0,0 +1,160 @@ +getQueryBuilderForTable('pages'); + + $additionalWhere = ' AND deleted = 0'; + $additionalParams = [ + 'limit' => $maxResults, + ]; + + if ($search) { + $additionalWhere .= ' AND (title LIKE :search OR uid = :uid)'; + $additionalParams['search'] = '%' . $search . '%'; + $additionalParams['uid'] = $search; + } + + if ($status) { + $additionalWhere .= ' AND tx_ximatypo3contentplanner_status = :status'; + $additionalParams['status'] = $status; + } + + if ($assignee) { + $additionalWhere .= ' AND tx_ximatypo3contentplanner_assignee = :assignee'; + $additionalParams['assignee'] = $assignee; + } + + $sqlArray = []; + + foreach (ExtensionUtility::getRecordTables() as $table) { + if ($type && $type !== $table) { + continue; + } + + $this->getSqlByTable($table, $sqlArray, $additionalWhere); + } + + $sql = implode(' UNION ', $sqlArray) . ' ORDER BY tstamp DESC LIMIT :limit'; + + $statement = $queryBuilder->getConnection()->executeQuery($sql, $additionalParams); + $results = $statement->fetchAllAssociative(); + + foreach ($results as $key => $record) { + if (!PermissionUtility::checkAccessForRecord($record['tablename'], $record)) { + unset($results[$key]); + } + } + return $results; + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function findByPid(string $table, ?int $pid = null, bool $orderByTstamp = true): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + + $query = $queryBuilder + ->select('uid', $this->getTitleField($table) . ' as title', 'tx_ximatypo3contentplanner_status', 'tx_ximatypo3contentplanner_assignee', 'tx_ximatypo3contentplanner_comments') + ->from($table) + ->andWhere( + $queryBuilder->expr()->isNotNull('tx_ximatypo3contentplanner_status'), + $queryBuilder->expr()->neq('tx_ximatypo3contentplanner_status', 0), + $queryBuilder->expr()->eq('deleted', 0) + ); + + if ($orderByTstamp) { + $query->addOrderBy('tstamp', 'DESC'); + } + + if ($pid) { + $query->andWhere( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) + ); + } + + return $query->executeQuery() + ->fetchAllAssociative(); + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function findByUid(?string $table, ?int $uid): array|bool|null + { + if (!$table && !$uid) { + return null; + } + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + + $query = $queryBuilder + ->select('uid', $this->getTitleField($table) . ' as "title"', 'tx_ximatypo3contentplanner_status', 'tx_ximatypo3contentplanner_assignee', 'tx_ximatypo3contentplanner_comments') + ->from($table) + ->andWhere( + $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)), + $queryBuilder->expr()->eq('deleted', 0) + ); + + return $query->executeQuery() + ->fetchAssociative(); + } + + public function updateStatusByUid(string $table, int $uid, int $status, int|bool|null $assignee = false): void + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder + ->update($table) + ->set('tx_ximatypo3contentplanner_status', $status) + ->where( + $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) + ); + + if ($assignee !== false) { + $queryBuilder->set('tx_ximatypo3contentplanner_assignee', $assignee); + } + $queryBuilder->executeQuery(); + } + + private function getSqlByTable(string $table, array &$sql, string $additionalWhere): void + { + $titleField = $this->getTitleField($table); + + if ($table === 'pages') { + $selects = array_merge($this->defaultSelects, [$titleField . ' as title, "' . $table . '" as tablename', 'perms_userid', 'perms_groupid', 'perms_user', 'perms_group', 'perms_everybody']); + } else { + $selects = array_merge($this->defaultSelects, [$titleField . ' as title, "' . $table . '" as tablename', '0 as perms_userid', '0 as perms_groupid', '0 as perms_user', '0 as perms_group', '0 as perms_everybody']); + } + + $sql[] = '(SELECT ' . implode(',', $selects) . ' FROM ' . $table . ' WHERE tx_ximatypo3contentplanner_status IS NOT NULL AND tx_ximatypo3contentplanner_status != 0' . $additionalWhere . ')'; + } + + private function getTitleField(string $table): string + { + return $GLOBALS['TCA'][$table]['ctrl']['label']; + } +} diff --git a/Classes/Domain/Repository/StatusRepository.php b/Classes/Domain/Repository/StatusRepository.php index ea3b965..35ad9ea 100644 --- a/Classes/Domain/Repository/StatusRepository.php +++ b/Classes/Domain/Repository/StatusRepository.php @@ -4,13 +4,21 @@ namespace Xima\XimaTypo3ContentPlanner\Domain\Repository; +use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; use TYPO3\CMS\Extbase\Persistence\QueryInterface; use TYPO3\CMS\Extbase\Persistence\Repository; +use Xima\XimaTypo3ContentPlanner\Configuration; +use Xima\XimaTypo3ContentPlanner\Domain\Model\Status; class StatusRepository extends Repository { + public function __construct(private FrontendInterface $cache) + { + parent::__construct(); + } + protected $defaultOrderings = [ 'sorting' => QueryInterface::ORDER_ASCENDING, ]; @@ -21,4 +29,51 @@ public function initializeObject(): void $querySettings->setRespectStoragePage(false); $this->setDefaultQuerySettings($querySettings); } + + public function findAll(): array + { + $cacheIdentifier = Configuration::CACHE_IDENTIFIER . '--all'; + if ($this->cache->has($cacheIdentifier)) { + return $this->cache->get($cacheIdentifier); + } + + $query = $this->createQuery(); + $result = $query->execute()->toArray(); + $this->cache->set($cacheIdentifier, $result, $this->collectCacheTags($result)); + return $result; + } + + public function findByUid($uid): ?Status + { + $cacheIdentifier = Configuration::CACHE_IDENTIFIER . '--' . $uid; + if ($this->cache->has($cacheIdentifier)) { + return $this->cache->get($cacheIdentifier); + } + + $query = $this->createQuery(); + $query->matching($query->equals('uid', $uid)); + $result = $query->execute()->getFirst(); + + if ($result === null) { + return null; + } + $this->cache->set($cacheIdentifier, $result, $this->collectCacheTags([$result])); + return $result; + } + + public function findByTitle(string $title): ?Status + { + $query = $this->createQuery(); + $query->matching($query->equals('title', $title)); + return $query->execute()->getFirst(); + } + + private function collectCacheTags(array $data): array + { + $tags = []; + foreach ($data as $item) { + $tags[] = 'tx_ximatypo3contentplanner_domain_model_status_' . $item->getUid(); + } + return $tags; + } } diff --git a/Classes/Event/PrepareStatusSelectionEvent.php b/Classes/Event/PrepareStatusSelectionEvent.php new file mode 100644 index 0000000..91e1cc1 --- /dev/null +++ b/Classes/Event/PrepareStatusSelectionEvent.php @@ -0,0 +1,75 @@ +getSelection(); +* +* if ($event->getCurrentStatus() && $event->getCurrentStatus()->getTitle() === 'Needs review') { +* $targetStatus = $this->statusRepository->findOneByTitle('Pending'); +* +* if ($targetStatus) { +* unset($selection[$targetStatus->getUid()]); +* } +* } +* +* $event->setSelection($selection); +* ``` +*/ +class PrepareStatusSelectionEvent +{ + final public const NAME = 'xima_typo3_content_planner.status.prepare_selection'; + + public function __construct( + protected string $table, + protected int $uid, + protected object $context, + protected array $selection, + protected ?Status $currentStatus, + ) { + } + + public function getTable(): string + { + return $this->table; + } + + public function getUid(): int + { + return $this->uid; + } + + public function getSelection(): array + { + return $this->selection; + } + + public function setSelection(array $selection): void + { + $this->selection = $selection; + } + + /** + * @return \Xima\XimaTypo3ContentPlanner\Domain\Model\Status|null + */ + public function getCurrentStatus(): ?Status + { + return $this->currentStatus; + } + + public function getContext(): object + { + return $this->context; + } +} diff --git a/Classes/Event/StatusChangeEvent.php b/Classes/Event/StatusChangeEvent.php new file mode 100644 index 0000000..044b39e --- /dev/null +++ b/Classes/Event/StatusChangeEvent.php @@ -0,0 +1,83 @@ +getFieldArray(); +* +* if ($event->getNewStatus() && $event->getNewStatus()->getTitle() === 'Final review') { +* // Assign the record to a specific user, e.g. the chief editor +* $fieldArray['tx_ximatypo3contentplanner_assignee'] = 42; +* } +* +* $event->setFieldArray($fieldArray); +* ``` +*/ +class StatusChangeEvent +{ + final public const NAME = 'xima_typo3_content_planner.status.change'; + + public function __construct( + protected string $table, + protected int $uid, + protected array $fieldArray, + protected ?Status $previousStatus, + protected ?Status $newStatus + ) { + } + + public function getTable(): string + { + return $this->table; + } + + public function getUid(): int + { + return $this->uid; + } + + public function getFieldArray(): array + { + return $this->fieldArray; + } + + public function setFieldArray(array $fieldArray): void + { + $this->fieldArray = $fieldArray; + } + + /** + * @return \Xima\XimaTypo3ContentPlanner\Domain\Model\Status|null + */ + public function getPreviousStatus(): ?Status + { + return $this->previousStatus; + } + + /** + * @return \Xima\XimaTypo3ContentPlanner\Domain\Model\Status|null + */ + public function getNewStatus(): ?Status + { + return $this->newStatus; + } + + /** + * @param \Xima\XimaTypo3ContentPlanner\Domain\Model\Status|null $newStatus + */ + public function setNewStatus(?Status $newStatus): void + { + $this->newStatus = $newStatus; + } +} diff --git a/Classes/EventListener/AfterPageTreeItemsPreparedListener.php b/Classes/EventListener/AfterPageTreeItemsPreparedListener.php index 47a9ea9..9da1d1b 100644 --- a/Classes/EventListener/AfterPageTreeItemsPreparedListener.php +++ b/Classes/EventListener/AfterPageTreeItemsPreparedListener.php @@ -7,7 +7,8 @@ use TYPO3\CMS\Backend\Controller\Event\AfterPageTreeItemsPreparedEvent; use TYPO3\CMS\Core\Utility\VersionNumberUtility; use Xima\XimaTypo3ContentPlanner\Configuration; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; +use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; use Xima\XimaTypo3ContentPlanner\Utility\VisibilityUtility; /* @@ -15,6 +16,10 @@ */ final class AfterPageTreeItemsPreparedListener { + public function __construct(protected readonly StatusRepository $statusRepository) + { + } + public function __invoke(AfterPageTreeItemsPreparedEvent $event): void { if (!VisibilityUtility::checkContentStatusVisibility()) { @@ -23,24 +28,43 @@ public function __invoke(AfterPageTreeItemsPreparedEvent $event): void $items = $event->getItems(); foreach ($items as &$item) { + $status = null; + $version = VersionNumberUtility::getCurrentTypo3Version(); if (isset($item['_page']['tx_ximatypo3contentplanner_status'])) { - $status = ContentUtility::getStatus($item['_page']['tx_ximatypo3contentplanner_status']); + $status = $this->statusRepository->findByUid($item['_page']['tx_ximatypo3contentplanner_status']); if ($status) { - $version = VersionNumberUtility::getCurrentTypo3Version(); if (version_compare($version, '13.0.0', '>=')) { // @phpstan-ignore-next-line $item['labels'][] = new \TYPO3\CMS\Backend\Dto\Tree\Label\Label( label: $status->getTitle(), - color: Configuration::STATUS_COLOR_CODES[$status->getColor()], - priority: 1, + color: Configuration\Colors::get($status->getColor()), ); + if (ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_CURRENT_ASSIGNEE_HIGHLIGHT)) { + // @phpstan-ignore-next-line + $item['statusInformation'][] = new \TYPO3\CMS\Backend\Dto\Tree\Status\StatusInformation( + label: $GLOBALS['LANG']->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_be.xlf:currentAssignee'), + severity: \TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::WARNING, + icon: 'actions-dot', + ); + } } else { - $item['backgroundColor'] = Configuration::STATUS_COLOR_CODES[$status->getColor()]; + $item['backgroundColor'] = Configuration\Colors::get($status->getColor(), true); } } + } else { + if (version_compare($version, '13.0.0', '>=')) { + // Workaround for label behavior in TYPO3 13 + // Labels will be inherited from parent pages, if not set explicitly + // Currently there is no way to suppress this behavior + // @see https://github.com/TYPO3/typo3/blob/5619d59f00808f7bec7a311106fda6a52854c0bd/Build/Sources/TypeScript/backend/tree/tree.ts#L1224 + // @phpstan-ignore-next-line + $item['labels'][] = new \TYPO3\CMS\Backend\Dto\Tree\Label\Label( + label: '', + color: 'inherit', + ); + } } } - $event->setItems($items); } } diff --git a/Classes/EventListener/DrawBackendHeaderListener.php b/Classes/EventListener/DrawBackendHeaderListener.php index 0a696bb..5e8c2be 100644 --- a/Classes/EventListener/DrawBackendHeaderListener.php +++ b/Classes/EventListener/DrawBackendHeaderListener.php @@ -10,8 +10,11 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Fluid\View\StandaloneView; use Xima\XimaTypo3ContentPlanner\Configuration; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\BackendUserRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\CommentRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\RecordRepository; use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; use Xima\XimaTypo3ContentPlanner\Utility\VisibilityUtility; /* @@ -20,8 +23,13 @@ final class DrawBackendHeaderListener { - public function __construct(protected PageRepository $pageRepository, protected StatusRepository $statusRepository) - { + public function __construct( + private readonly PageRepository $pageRepository, + private readonly StatusRepository $statusRepository, + private readonly CommentRepository $commentRepository, + private readonly BackendUserRepository $backendUserRepository, + private readonly RecordRepository $recordRepository + ) { } public function __invoke(ModifyPageLayoutContentEvent $event): void @@ -38,31 +46,42 @@ public function __invoke(ModifyPageLayoutContentEvent $event): void if (!$pageInfo['tx_ximatypo3contentplanner_status']) { return; } - $status = ContentUtility::getStatus($pageInfo['tx_ximatypo3contentplanner_status']); + $status = $this->statusRepository->findByUid($pageInfo['tx_ximatypo3contentplanner_status']); if (!$status) { return; } $view = GeneralUtility::makeInstance(StandaloneView::class); - $view->setTemplatePathAndFilename('EXT:' . Configuration::EXT_KEY . '/Resources/Private/Templates/Backend/PageHeader/PageHeaderInfo.html'); + $view->setTemplatePathAndFilename('EXT:' . Configuration::EXT_KEY . '/Resources/Private/Templates/Backend/Header/HeaderInfo.html'); /** @var PageRenderer $pageRenderer */ $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class); $pageRenderer->loadJavaScriptModule('@xima/ximatypo3contentplanner/new-comment-modal.js'); $pageRenderer->loadJavaScriptModule('@xima/ximatypo3contentplanner/comments-modal.js'); + $pageRenderer->addCssFile('EXT:' . Configuration::EXT_KEY . '/Resources/Public/Css/Header.css'); $pageRenderer->addInlineLanguageLabelFile('EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf'); $view->assignMultiple([ + 'mode' => 'pageHeader', 'data' => $pageInfo, - 'assignee' => ContentUtility::getBackendUsernameById((int)$pageInfo['tx_ximatypo3contentplanner_assignee']), + 'assignee' => $this->backendUserRepository->getUsernameByUid((int)$pageInfo['tx_ximatypo3contentplanner_assignee']), + 'assignedToCurrentUser' => $this->getAssignedToCurrentUser((int)$pageInfo['tx_ximatypo3contentplanner_assignee']), 'icon' => $status->getColoredIcon(), - 'type' => Configuration::STATUS_COLOR_ALERTS[$status->getColor()], 'status' => $status, - 'comments' => $pageInfo['tx_ximatypo3contentplanner_comments'] ? ContentUtility::getComments($id, 'pages') : [], + 'comments' => $pageInfo['tx_ximatypo3contentplanner_comments'] ? $this->commentRepository->findAllByRecord($id, 'pages') : [], 'pid' => $id, 'userid' => $GLOBALS['BE_USER']->user['uid'], + 'contentElements' => ExtensionUtility::isRegisteredRecordTable('tt_content') ? $this->recordRepository->findByPid('tt_content', $id, false) : null, ]); $event->addHeaderContent($view->render()); } + + private function getAssignedToCurrentUser(int $assignee): bool + { + if (!ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_CURRENT_ASSIGNEE_HIGHLIGHT)) { + return false; + } + return $assignee === $GLOBALS['BE_USER']->user['uid']; + } } diff --git a/Classes/EventListener/ModifyButtonBarEventListener.php b/Classes/EventListener/ModifyButtonBarEventListener.php index c04f2a0..f83660e 100644 --- a/Classes/EventListener/ModifyButtonBarEventListener.php +++ b/Classes/EventListener/ModifyButtonBarEventListener.php @@ -12,15 +12,26 @@ use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Localization\LanguageService; use TYPO3\CMS\Core\Utility\GeneralUtility; +use Xima\XimaTypo3ContentPlanner\Configuration; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\BackendUserRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\RecordRepository; use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; +use Xima\XimaTypo3ContentPlanner\Manager\StatusSelectionManager; use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; +use Xima\XimaTypo3ContentPlanner\Utility\UrlHelper; use Xima\XimaTypo3ContentPlanner\Utility\VisibilityUtility; final class ModifyButtonBarEventListener { - public function __construct(private IconFactory $iconFactory, private UriBuilder $uriBuilder, protected StatusRepository $statusRepository) - { + public function __construct( + private readonly IconFactory $iconFactory, + private readonly UriBuilder $uriBuilder, + private readonly StatusRepository $statusRepository, + private readonly RecordRepository $recordRepository, + private readonly BackendUserRepository $backendUserRepository, + private readonly StatusSelectionManager $statusSelectionManager + ) { } public function __invoke(ModifyButtonBarEvent $event): void @@ -55,19 +66,16 @@ public function __invoke(ModifyButtonBarEvent $event): void if ($table === 'pages') { $uid = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? (isset($request->getQueryParams()['edit']['pages']) ? array_keys($request->getQueryParams()['edit']['pages'])[0] : 0)); - $page = ContentUtility::getPage($uid); - if (!$page) { - return; - } - $status = $page['tx_ximatypo3contentplanner_status'] ? ContentUtility::getStatus($page['tx_ximatypo3contentplanner_status']) : null; + $record = ContentUtility::getPage($uid); } else { $uid = (int)array_key_first($request->getQueryParams()['edit'][$table]); - $record = ContentUtility::getExtensionRecord($table, $uid); - if (!$record) { - return; - } - $status = $record['tx_ximatypo3contentplanner_status'] ? ContentUtility::getStatus($record['tx_ximatypo3contentplanner_status']) : null; + $record = $this->recordRepository->findByUid($table, $uid); + } + if (!$record) { + return; } + $status = $record['tx_ximatypo3contentplanner_status'] ? $this->statusRepository->findByUid($record['tx_ximatypo3contentplanner_status']) : null; + $buttonBar = $event->getButtonBar(); $buttons = $event->getButtons(); $buttons['right'] ??= []; @@ -78,6 +86,7 @@ public function __invoke(ModifyButtonBarEvent $event): void $status ? $status->getColoredIcon() : 'flag-gray' )); + $buttonsToAdd = []; foreach ($this->statusRepository->findAll() as $statusItem) { /** @var DropDownItemInterface $statusDropDownItem */ $statusDropDownItem = GeneralUtility::makeInstance(DropDownItem::class) @@ -114,9 +123,9 @@ public function __invoke(ModifyButtonBarEvent $event): void ] ) ); - $dropDownButton->addItem($statusDropDownItem); + $buttonsToAdd[$statusItem->getUid()] = $statusDropDownItem; } - $dropDownButton->addItem(GeneralUtility::makeInstance(DropDownDivider::class)); + $buttonsToAdd['divider'] = GeneralUtility::makeInstance(DropDownDivider::class); /** @var DropDownItemInterface $statusDropDownItem */ $statusDropDownItem = GeneralUtility::makeInstance(DropDownItem::class) @@ -153,7 +162,49 @@ public function __invoke(ModifyButtonBarEvent $event): void ] ) ); - $dropDownButton->addItem($statusDropDownItem); + $buttonsToAdd['reset'] = $statusDropDownItem; + + if (ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_EXTEND_CONTEXT_MENU)) { + if ($record['tx_ximatypo3contentplanner_assignee'] || $record['tx_ximatypo3contentplanner_comments']) { + $buttonsToAdd['divider2'] = GeneralUtility::makeInstance(DropDownDivider::class); + } + + // remove current status from list + if (in_array($record['tx_ximatypo3contentplanner_status'], array_keys($buttonsToAdd), true)) { + unset($buttonsToAdd[$record['tx_ximatypo3contentplanner_status']]); + } + + // remove reset if status is already null + if ($record['tx_ximatypo3contentplanner_status'] === null) { + unset($buttonsToAdd['divider']); + unset($buttonsToAdd['reset']); + } + + // assignee + if ($record['tx_ximatypo3contentplanner_assignee']) { + $username = $this->backendUserRepository->getUsernameByUid($record['tx_ximatypo3contentplanner_assignee']); + $assigneeDropDownItem = GeneralUtility::makeInstance(DropDownItem::class) + ->setLabel($username) + ->setIcon($this->iconFactory->getIcon('actions-user')) + ->setHref(UrlHelper::getContentStatusPropertiesEditUrl($table, $uid)); + $buttonsToAdd['assignee'] = $assigneeDropDownItem; + } + + // comments + if ($record['tx_ximatypo3contentplanner_status'] !== null) { + $commentsDropDownItem = GeneralUtility::makeInstance(DropDownItem::class) + ->setLabel($this->getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_be.xlf:comments') . ($record['tx_ximatypo3contentplanner_comments'] ? ' (' . $record['tx_ximatypo3contentplanner_comments'] . ')' : '')) + ->setIcon($this->iconFactory->getIcon('actions-message')) + ->setAttributes(['data-id' => $uid, 'data-table' => $table, 'data-new-comment-uri' => UrlHelper::getNewCommentUrl($table, $uid), 'data-content-planner-comments' => true, 'data-force-ajax-url' => true]) + ->setHref(UrlHelper::getContentStatusPropertiesEditUrl($table, $uid)); + $buttonsToAdd['comments'] = $commentsDropDownItem; + } + } + + $this->statusSelectionManager->prepareStatusSelection($this, $table, $uid, $buttonsToAdd, $record['tx_ximatypo3contentplanner_status']); + foreach ($buttonsToAdd as $buttonToAdd) { + $dropDownButton->addItem($buttonToAdd); + } $buttons['right'][] = [$dropDownButton]; $event->setButtons($buttons); @@ -169,6 +220,8 @@ private function removeButtonsExceptSave(ModifyButtonBarEvent $event): void } foreach ($buttonGroup as $button) { if ($button[0] instanceof InputButton && str_contains($button[0]->getName(), '_save')) { + $button[0]->setTitle($this->getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_be.xlf:save_and_close')); + $buttons[$position][] = $button; } } diff --git a/Classes/EventListener/ModifyRecordListRecordActionsListener.php b/Classes/EventListener/ModifyRecordListRecordActionsListener.php index f0d7f26..2552556 100644 --- a/Classes/EventListener/ModifyRecordListRecordActionsListener.php +++ b/Classes/EventListener/ModifyRecordListRecordActionsListener.php @@ -10,15 +10,25 @@ use TYPO3\CMS\Core\Localization\LanguageService; use Xima\XimaTypo3ContentPlanner\Configuration; use Xima\XimaTypo3ContentPlanner\Domain\Model\Dto\StatusItem; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\BackendUserRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\RecordRepository; use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Manager\StatusSelectionManager; +use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; +use Xima\XimaTypo3ContentPlanner\Utility\UrlHelper; use Xima\XimaTypo3ContentPlanner\Utility\VisibilityUtility; final class ModifyRecordListRecordActionsListener { protected ServerRequest $request; - public function __construct(private IconFactory $iconFactory, private UriBuilder $uriBuilder, protected StatusRepository $statusRepository) - { + public function __construct( + private readonly IconFactory $iconFactory, + private readonly UriBuilder $uriBuilder, + private readonly StatusRepository $statusRepository, + private readonly RecordRepository $recordRepository, + private readonly BackendUserRepository $backendUserRepository, + private readonly StatusSelectionManager $statusSelectionManager + ) { $this->request = $GLOBALS['TYPO3_REQUEST']; } @@ -33,14 +43,14 @@ public function __invoke(ModifyRecordListRecordActionsEvent $event): void $uid = $event->getRecord()['uid']; // ToDo: this is necessary cause the status is not in the record, pls check tca for this - $record = ContentUtility::getExtensionRecord($table, $uid); + $record = $this->recordRepository->findByUid($table, $uid); if (!is_array($record)) { return; } $statusId = $record['tx_ximatypo3contentplanner_status']; $statusItem = StatusItem::create($record); - $status = ContentUtility::getStatus($statusId); + $status = $this->statusRepository->findByUid($statusId); $title = $status ? $status->getTitle() : 'Status'; $icon = $status ? $status->getColoredIcon() : 'flag-gray'; @@ -48,6 +58,8 @@ public function __invoke(ModifyRecordListRecordActionsEvent $event): void '; - if ((bool)$record['tx_ximatypo3contentplanner_assignee']) { - $action .= ' - ' . $statusItem->getAssigneeAvatar() . ' ' . $statusItem->getAssigneeName() . ''; + if (ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_EXTEND_CONTEXT_MENU)) { + if ($record['tx_ximatypo3contentplanner_assignee'] || $record['tx_ximatypo3contentplanner_comments']) { + $actionsToAdd['divider2'] = '
  • '; + } + + // remove current status from list + if (in_array($record['tx_ximatypo3contentplanner_status'], array_keys($actionsToAdd), true)) { + unset($actionsToAdd[$record['tx_ximatypo3contentplanner_status']]); + } + + // remove reset if status is already null + if ($record['tx_ximatypo3contentplanner_status'] === null) { + unset($actionsToAdd['divider']); + unset($actionsToAdd['reset']); + } + + // assignee + if ($record['tx_ximatypo3contentplanner_assignee']) { + $username = $this->backendUserRepository->getUsernameByUid($record['tx_ximatypo3contentplanner_assignee']); + $actionsToAdd['assignee'] = '
  • ' . $statusItem->getAssigneeAvatar() . ' ' . $statusItem->getAssigneeName() . '
  • '; + } + + // comments + if ($record['tx_ximatypo3contentplanner_comments']) { + $actionsToAdd['comments'] = '
  • ' . $this->iconFactory->getIcon('content-message', Icon::SIZE_SMALL)->render() . ' ' . $record['tx_ximatypo3contentplanner_comments'] . '
  • '; + } } - if ((bool)$record['tx_ximatypo3contentplanner_comments']) { - $action .= ' - ' . $this->iconFactory->getIcon('content-message', Icon::SIZE_SMALL)->render() . ' ' . $record['tx_ximatypo3contentplanner_comments'] . ''; + + $this->statusSelectionManager->prepareStatusSelection($this, $table, $uid, $actionsToAdd, $record['tx_ximatypo3contentplanner_status']); + foreach ($actionsToAdd as $actionToAdd) { + $action .= $actionToAdd; } + + $action .= ''; $action .= ''; $event->setAction( $action, @@ -112,16 +151,6 @@ public function __invoke(ModifyRecordListRecordActionsEvent $event): void } } - private function getEditUrl(string $table, int $uid): string - { - $params = [ - 'edit' => [$table => [$uid => 'edit']], - 'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(), - 'columnsOnly' => 'tx_ximatypo3contentplanner_status,tx_ximatypo3contentplanner_assignee,tx_ximatypo3contentplanner_comments', - ]; - return (string)$this->uriBuilder->buildUriFromRoute('record_edit', $params); - } - protected function getLanguageService(): LanguageService { return $GLOBALS['LANG']; diff --git a/Classes/EventListener/RenderAdditionalContentToRecordListListener.php b/Classes/EventListener/RenderAdditionalContentToRecordListListener.php index f222456..eec4a92 100644 --- a/Classes/EventListener/RenderAdditionalContentToRecordListListener.php +++ b/Classes/EventListener/RenderAdditionalContentToRecordListListener.php @@ -4,12 +4,17 @@ use TYPO3\CMS\Backend\Controller\Event\RenderAdditionalContentToRecordListEvent; use Xima\XimaTypo3ContentPlanner\Configuration; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\RecordRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; use Xima\XimaTypo3ContentPlanner\Utility\VisibilityUtility; final class RenderAdditionalContentToRecordListListener { + public function __construct(private readonly StatusRepository $statusRepository, private readonly RecordRepository $recordRepository) + { + } + public function __invoke(RenderAdditionalContentToRecordListEvent $event): void { if (!VisibilityUtility::checkContentStatusVisibility()) { @@ -29,10 +34,10 @@ public function __invoke(RenderAdditionalContentToRecordListEvent $event): void return; } - $records[$table] = ContentUtility::getExtensionRecords($table, $pid); + $records[$table] = $this->recordRepository->findByPid($table, $pid); } else { foreach (ExtensionUtility::getRecordTables() as $table) { - $records[$table] = ContentUtility::getExtensionRecords($table, $pid); + $records[$table] = $this->recordRepository->findByPid($table, $pid); } } @@ -43,9 +48,9 @@ public function __invoke(RenderAdditionalContentToRecordListEvent $event): void continue; } foreach ($tableRecords as $tableRecord) { - $status = ContentUtility::getStatus($tableRecord['tx_ximatypo3contentplanner_status']); + $status = $this->statusRepository->findByUid($tableRecord['tx_ximatypo3contentplanner_status']); if ($status) { - $additionalCss .= 'tr[data-table="' . $table . '"][data-uid="' . $tableRecord['uid'] . '"] > td { background-color: ' . Configuration::STATUS_COLOR_CODES[$status->getColor()] . '; } '; + $additionalCss .= 'tr[data-table="' . $table . '"][data-uid="' . $tableRecord['uid'] . '"] > td { background-color: ' . Configuration\Colors::get($status->getColor(), true) . '; } '; } } } diff --git a/Classes/Hooks/DataHandlerHook.php b/Classes/Hooks/DataHandlerHook.php index 23da1ad..31011d9 100644 --- a/Classes/Hooks/DataHandlerHook.php +++ b/Classes/Hooks/DataHandlerHook.php @@ -4,14 +4,18 @@ namespace Xima\XimaTypo3ContentPlanner\Hooks; +use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\Utility\MathUtility; -use Xima\XimaTypo3ContentPlanner\Configuration; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Manager\StatusChangeManager; use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; final class DataHandlerHook { + public function __construct(private FrontendInterface $cache, private readonly StatusChangeManager $statusChangeManager) + { + } + /** * Hook: processDatamap_preProcessFieldArray */ @@ -22,7 +26,11 @@ public function processDatamap_preProcessFieldArray(array &$incomingFieldArray, } if (ExtensionUtility::isRegisteredRecordTable($table)) { - $this->processContentPlannerFields($incomingFieldArray, $table, $id); + $this->statusChangeManager->processContentPlannerFields($incomingFieldArray, $table, $id); + } + + if (in_array('tx_ximatypo3contentplanner_comment', $dataHandler->datamap)) { + $this->fixNewCommentEntry($dataHandler); } } @@ -37,7 +45,7 @@ public function processCmdmap_preProcess($command, $table, $id, $value, DataHand if ($command === 'delete' && $table === 'tx_ximatypo3contentplanner_status') { // Clear all status of records that are assigned to the deleted status foreach (ExtensionUtility::getRecordTables() as $table) { - ContentUtility::clearStatusOfExtensionRecords($table, $id); + $this->statusChangeManager->clearStatusOfExtensionRecords($table, $id); } } } @@ -50,38 +58,38 @@ public function processDatamap_beforeStart(DataHandler $dataHandler): void $datamap = $dataHandler->datamap; // Workaround to solve relation of comments created within the modal if (array_key_first($datamap) === 'tx_ximatypo3contentplanner_comment') { - $id = array_key_first($datamap['tx_ximatypo3contentplanner_comment']); - if (!MathUtility::canBeInterpretedAsInteger($id) && !array_key_exists('pages', $dataHandler->datamap) && $datamap['tx_ximatypo3contentplanner_comment'][$id]['foreign_table'] === 'pages') { - $dataHandler->datamap['pages'][$datamap['tx_ximatypo3contentplanner_comment'][$id]['pid']]['tx_ximatypo3contentplanner_comments'] = $id; - // Set author to current user - $dataHandler->datamap['tx_ximatypo3contentplanner_comment'][$id]['author'] = $GLOBALS['BE_USER']->getUserId(); - } + $this->fixNewCommentEntry($dataHandler); } } - private function processContentPlannerFields(array &$incomingFieldArray, $table, $id): void + public function clearCachePostProc(array $params): void { - if (!isset($incomingFieldArray['tx_ximatypo3contentplanner_status'])) { - return; - } - if (array_key_exists('tx_ximatypo3contentplanner_assignee', $incomingFieldArray) && ($incomingFieldArray['tx_ximatypo3contentplanner_assignee'] === '' || $incomingFieldArray['tx_ximatypo3contentplanner_assignee'] === 0)) { - $incomingFieldArray['tx_ximatypo3contentplanner_assignee'] = null; - } + $this->cache->flushByTags(array_keys($params['tags'])); + } - if ($incomingFieldArray['tx_ximatypo3contentplanner_status'] === '' || $incomingFieldArray['tx_ximatypo3contentplanner_status'] === 0) { - $incomingFieldArray['tx_ximatypo3contentplanner_status'] = null; + private function fixNewCommentEntry(&$dataHandler): void + { + $id = null; + foreach (array_keys($dataHandler->datamap['tx_ximatypo3contentplanner_comment']) as $key) { + if (!MathUtility::canBeInterpretedAsInteger($key)) { + $id = $key; + } } - // auto reset assignee if status is set to null - if ($incomingFieldArray['tx_ximatypo3contentplanner_status'] === null && $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['features']['autoAssignment']) { - $incomingFieldArray['tx_ximatypo3contentplanner_assignee'] = null; - } + if (array_key_exists('tx_ximatypo3contentplanner_comment', $dataHandler->defaultValues) && $id !== null) { + $dataHandler->datamap['tx_ximatypo3contentplanner_comment'][$id]['author'] = $GLOBALS['BE_USER']->getUserId(); + $table = null; + // @ToDo: Why are default values doesn't seem to be set as expected? + foreach ($dataHandler->defaultValues['tx_ximatypo3contentplanner_comment'] as $key => $value) { + if ($key === 'foreign_table') { + $table = $value; + } + $dataHandler->datamap['tx_ximatypo3contentplanner_comment'][$id][$key] = $value; + } - // auto assign user if status is initially set - if (array_key_exists('tx_ximatypo3contentplanner_assignee', $incomingFieldArray) && $incomingFieldArray['tx_ximatypo3contentplanner_status'] !== null && $incomingFieldArray['tx_ximatypo3contentplanner_assignee'] === null && $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['features']['autoUnassignment']) { - $preRecord = ContentUtility::getExtensionRecord($table, $id); - if ($preRecord['tx_ximatypo3contentplanner_status'] === null || $preRecord['tx_ximatypo3contentplanner_status'] === 0) { - $incomingFieldArray['tx_ximatypo3contentplanner_assignee'] = $GLOBALS['BE_USER']->getUserId(); + // @ToDo: how to fix this for other tables? + if ($table === 'pages') { + $dataHandler->datamap[$table][$dataHandler->datamap['tx_ximatypo3contentplanner_comment'][$id]['pid']]['tx_ximatypo3contentplanner_comments'] = $id; } } } diff --git a/Classes/Manager/StatusChangeManager.php b/Classes/Manager/StatusChangeManager.php new file mode 100644 index 0000000..01ca6b6 --- /dev/null +++ b/Classes/Manager/StatusChangeManager.php @@ -0,0 +1,103 @@ +nullableField($incomingFieldArray, 'tx_ximatypo3contentplanner_assignee'); + $this->nullableField($incomingFieldArray, 'tx_ximatypo3contentplanner_status'); + + // auto reset assignee if status is set to null + if ($incomingFieldArray['tx_ximatypo3contentplanner_status'] === null) { + $incomingFieldArray['tx_ximatypo3contentplanner_assignee'] = null; + + if (ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_CLEAR_COMMENTS_ON_STATUS_RESET)) { + $this->commentRepository->deleteAllCommentsByRecord($id, $table); + } + } + + $preRecord = $this->recordRepository->findByUid($table, $id); + + // auto assign user if status is initially set + if ((!array_key_exists('tx_ximatypo3contentplanner_assignee', $incomingFieldArray) || $incomingFieldArray['tx_ximatypo3contentplanner_assignee'] === null) + && $incomingFieldArray['tx_ximatypo3contentplanner_status'] !== null + && ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_AUTO_ASSIGN)) { + // Check if status was null before + // ToDo: Check if this is the correct way to get the previous status + if ($preRecord['tx_ximatypo3contentplanner_status'] === null + || $preRecord['tx_ximatypo3contentplanner_status'] === 0) { + $incomingFieldArray['tx_ximatypo3contentplanner_assignee'] = $GLOBALS['BE_USER']->getUserId(); + } + } + + if ($this->isStatusFieldChanged($incomingFieldArray, $preRecord)) { + $previousStatus = $preRecord['tx_ximatypo3contentplanner_status'] ? ContentUtility::getStatus($preRecord['tx_ximatypo3contentplanner_status']) : null; + $newStatus = $incomingFieldArray['tx_ximatypo3contentplanner_status'] ? ContentUtility::getStatus((int)$incomingFieldArray['tx_ximatypo3contentplanner_status']) : null; + $this->eventDispatcher->dispatch(new StatusChangeEvent($table, $id, $incomingFieldArray, $previousStatus, $newStatus)); + + if ($incomingFieldArray['tx_ximatypo3contentplanner_status'] === null && ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_RESET_CONTENT_ELEMENT_STATUS_ON_PAGE_RESET)) { + $this->clearStatusOfExtensionRecords('tt_content', pid: $id); + } + } + } + + public function clearStatusOfExtensionRecords(string $table, ?int $status = null, ?int $pid = null): void + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder + ->update($table) + ->set('tx_ximatypo3contentplanner_status', null) + ; + + if ($status) { + $queryBuilder->andWhere( + $queryBuilder->expr()->eq('tx_ximatypo3contentplanner_status', $status) + ); + } + + if ($pid) { + $queryBuilder->andWhere( + $queryBuilder->expr()->eq('pid', $pid) + ); + } + + $queryBuilder->executeQuery(); + } + + private function nullableField(array &$incomingFieldArray, string $field): void + { + if (array_key_exists($field, $incomingFieldArray) && ($incomingFieldArray[$field] === '' || $incomingFieldArray[$field] === 0)) { + $incomingFieldArray[$field] = null; + } + } + + private function isStatusFieldChanged(array $incomingFieldArray, array|bool $preRecord): bool + { + if (!is_array($preRecord)) { + return false; + } + return $preRecord['tx_ximatypo3contentplanner_status'] !== $incomingFieldArray['tx_ximatypo3contentplanner_status']; + } +} diff --git a/Classes/Manager/StatusSelectionManager.php b/Classes/Manager/StatusSelectionManager.php new file mode 100644 index 0000000..e362bcb --- /dev/null +++ b/Classes/Manager/StatusSelectionManager.php @@ -0,0 +1,27 @@ +eventDispatcher->dispatch(new PrepareStatusSelectionEvent($table, $uid, $context, $selection, $status)); + $selection = $event->getSelection(); + } +} diff --git a/Classes/Middleware/BackendContentModifierMiddleware.php b/Classes/Middleware/BackendContentModifierMiddleware.php index 4f65ad8..dbb939f 100644 --- a/Classes/Middleware/BackendContentModifierMiddleware.php +++ b/Classes/Middleware/BackendContentModifierMiddleware.php @@ -8,19 +8,54 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder; +use TYPO3\CMS\Core\Core\RequestId; use TYPO3\CMS\Core\Http\Response; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Fluid\View\StandaloneView; use Xima\XimaTypo3ContentPlanner\Configuration; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\BackendUserRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\CommentRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\RecordRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; +use Xima\XimaTypo3ContentPlanner\Utility\ExtensionUtility; +use Xima\XimaTypo3ContentPlanner\Utility\UrlHelper; class BackendContentModifierMiddleware implements MiddlewareInterface { + public function __construct( + private readonly StatusRepository $statusRepository, + private readonly RecordRepository $recordRepository, + private readonly BackendUserRepository $backendUserRepository, + private readonly CommentRepository $commentRepository, + private readonly RequestId $requestId + ) { + } + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { - if ($request->getAttribute('applicationType') === SystemEnvironmentBuilder::REQUESTTYPE_BE - && $request->getAttribute('module') !== null - && $request->getAttribute('module')->getIdentifier() === 'web_layout' - && in_array('tt_content', $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['registerAdditionalRecordTables'])) { + if (UrlHelper::isRelevantRecordEditRequest($request)) { + $response = $handler->handle($request); + $content = $response->getBody()->__toString(); + + if ($content !== '') { + $table = array_key_first($request->getQueryParams()['edit']); + $uid = $request->getQueryParams()['edit'][$table] ?? 0; + $uid = is_array($uid) ? (int)array_key_first($uid) : (int)$uid; + + $newContent = $this->addRecordEditHeader($table, $uid, $content); + if (!$newContent) { + return $response; + } + + $newResponse = new Response(); + $newResponse->getBody()->write($newContent); + + return $newResponse; + } + return $response; + } + + if (UrlHelper::isRelevantWebLayoutRequest($request)) { $response = $handler->handle($request); $content = $response->getBody()->__toString(); @@ -29,26 +64,65 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return $handler->handle($request); } - $styling = []; - $records = ContentUtility::getExtensionRecords('tt_content', (int)$pid); + $newResponse = new Response(); + $newResponse->getBody()->write($content . $this->addStatusHintToContentElement((int)$pid)); + + return $newResponse; + } - foreach ($records as $record) { - $status = ContentUtility::getStatus($record['tx_ximatypo3contentplanner_status']); - if (!$status) { - continue; - } - $statusColor = Configuration::STATUS_COLOR_CODES[$status->getColor()]; - $styling[] = '.t3-page-ce[data-uid="' . $record['uid'] . '"]:before { content: "";display:block;box-shadow:var(--pagemodule-element-box-shadow);padding:.5em;border-left: 5px solid ' . $statusColor . ';border-radius: 5px 5px 0 0;background-color:' . $statusColor . '; }'; + return $handler->handle($request); + } + + private function addRecordEditHeader(string $table, int $uid, string $content): string|bool + { + $record = $this->recordRepository->findByUid($table, $uid); + + if ($record) { + $status = $this->statusRepository->findByUid($record['tx_ximatypo3contentplanner_status']); + + if (!$status) { + return false; } - $content .= ''; + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplatePathAndFilename('EXT:' . Configuration::EXT_KEY . '/Resources/Private/Templates/Backend/Header/HeaderInfo.html'); - $newResponse = new Response(); - $newResponse->getBody()->write($content); + $view->assignMultiple([ + 'mode' => 'edit', + 'data' => $record, + 'assignee' => $this->backendUserRepository->getUsernameByUid((int)$record['tx_ximatypo3contentplanner_assignee']), + 'icon' => $status->getColoredIcon(), + 'status' => $status, + 'comments' => $record['tx_ximatypo3contentplanner_comments'] ? $this->commentRepository->findAllByRecord($uid, $table) : [], + 'pid' => $record['pid'], + 'userid' => $GLOBALS['BE_USER']->user['uid'], + ]); - return $newResponse; + $additionalContent = $view->render(); + $additionalContent .= ExtensionUtility::getCssTag('EXT:' . Configuration::EXT_KEY . '/Resources/Public/Css/Header.css', ['nonce' => $this->requestId->nonce]); + + /* + * This is a workaround to add the header content to the top of the record edit form. + */ + return preg_replace('/(handle($request); + private function addStatusHintToContentElement(int $pid): string + { + $styling = []; + $records = $this->recordRepository->findByPid('tt_content', (int)$pid); + + foreach ($records as $record) { + $status = $this->statusRepository->findByUid($record['tx_ximatypo3contentplanner_status']); + if (!$status) { + continue; + } + $statusColor = Configuration\Colors::get($status->getColor()); + $styling[] = '.t3-page-ce[data-uid="' . $record['uid'] . '"]:before { content: "";display:block;box-shadow:var(--pagemodule-element-box-shadow);padding:.5em;border-left: 5px solid ' . $statusColor . ';border-radius: 5px 5px 0 0;background-color:' . $statusColor . '; }'; + } + + return ''; } } diff --git a/Classes/Utility/ContentUtility.php b/Classes/Utility/ContentUtility.php index 121fc45..a5f4531 100644 --- a/Classes/Utility/ContentUtility.php +++ b/Classes/Utility/ContentUtility.php @@ -5,11 +5,11 @@ namespace Xima\XimaTypo3ContentPlanner\Utility; use TYPO3\CMS\Backend\Utility\BackendUtility; -use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Domain\Repository\PageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; -use Xima\XimaTypo3ContentPlanner\Domain\Model\Dto\CommentItem; use Xima\XimaTypo3ContentPlanner\Domain\Model\Status; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\BackendUserRepository; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\CommentRepository; use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; class ContentUtility @@ -23,208 +23,63 @@ public static function getStatus(?int $statusId): ?Status return $statusRepository->findByUid($statusId); } - public static function getPage(int $pageId): array|bool + public static function getStatusByTitle(?string $title): ?Status { - $pageRepository = GeneralUtility::makeInstance(PageRepository::class); - return $pageRepository->getPage($pageId); - } - - public static function getAssignedPages(): array|bool - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); - - return $queryBuilder - ->select('*') - ->from('pages') - ->where( - $queryBuilder->expr()->eq('tx_ximatypo3contentplanner_assignee', $queryBuilder->createNamedParameter($GLOBALS['BE_USER']->user['uid'], \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) - ) - ->executeQuery()->fetchAllAssociative(); - } - - public static function getRecordsByFilter(?string $search = null, ?int $status = null, ?int $assignee = null, ?string $type = null, int $maxResults = 20): array|bool - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); - - $additionalWhere = ' AND deleted = 0'; - $additionalParams = [ - 'limit' => $maxResults, - ]; - - $defaultSelects = [ - 'uid', - 'pid', - 'tstamp', - 'tx_ximatypo3contentplanner_status', - 'tx_ximatypo3contentplanner_assignee', - 'tx_ximatypo3contentplanner_comments', - ]; - - if ($search) { - $additionalWhere .= ' AND (title LIKE :search OR uid = :uid)'; - $additionalParams['search'] = '%' . $search . '%'; - $additionalParams['uid'] = $search; - } - if ($status) { - $additionalWhere .= ' AND tx_ximatypo3contentplanner_status = :status'; - $additionalParams['status'] = $status; - } - if ($assignee) { - $additionalWhere .= ' AND tx_ximatypo3contentplanner_assignee = :assignee'; - $additionalParams['assignee'] = $assignee; - } - $sqlArray = []; - foreach (ExtensionUtility::getRecordTables() as $table) { - if ($type && $type !== $table) { - continue; - } - - $titleField = $GLOBALS['TCA'][$table]['ctrl']['label']; - if ($table === 'pages') { - $selects = array_merge($defaultSelects, [$titleField . ' as title, "' . $table . '" as tablename', 'perms_userid', 'perms_groupid', 'perms_user', 'perms_group', 'perms_everybody']); - } else { - $selects = array_merge($defaultSelects, [$titleField . ' as title, "' . $table . '" as tablename', '0 as perms_userid', '0 as perms_groupid', '0 as perms_user', '0 as perms_group', '0 as perms_everybody']); - } - - $sqlArray[] = '(SELECT ' . implode(',', $selects) . ' FROM ' . $table . ' WHERE tx_ximatypo3contentplanner_status IS NOT NULL AND tx_ximatypo3contentplanner_status != 0' . $additionalWhere . ')'; - } - $sql = implode(' UNION ', $sqlArray) . ' ORDER BY tstamp DESC LIMIT :limit'; - - $statement = $queryBuilder->getConnection()->executeQuery($sql, $additionalParams); - $results = $statement->fetchAllAssociative(); - - foreach ($results as $key => $record) { - if (!PermissionUtility::checkAccessForRecord($record['tablename'], $record)) { - unset($results[$key]); - } + if (!$title) { + return null; } - return $results; + $statusRepository = GeneralUtility::makeInstance(StatusRepository::class); + return $statusRepository->findByTitle($title); } - public static function getComments(int $id, string $table): array + public static function getPage(int $pageId): array|bool { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_ximatypo3contentplanner_comment'); - - $comments = $queryBuilder - ->select('*') - ->from('tx_ximatypo3contentplanner_comment') - ->where( - $queryBuilder->expr()->eq('foreign_uid', $queryBuilder->createNamedParameter($id, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)), - $queryBuilder->expr()->eq('foreign_table', $queryBuilder->createNamedParameter($table, \TYPO3\CMS\Core\Database\Connection::PARAM_STR)), - $queryBuilder->expr()->eq('deleted', 0) - ) - ->orderBy('tstamp', 'DESC') - ->executeQuery()->fetchAllAssociative(); - - $items = []; - foreach ($comments as $result) { - try { - $items[] = CommentItem::create($result); - } catch (\Exception $e) { - } - } - - return $items; + $pageRepository = GeneralUtility::makeInstance(PageRepository::class); + return $pageRepository->getPage($pageId); } + /** + * @Deprecated + */ public static function getComment(int $id): array|bool { if (!$id) { return false; } - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_ximatypo3contentplanner_comment'); - - return $queryBuilder - ->select('*') - ->from('tx_ximatypo3contentplanner_comment') - ->where( - $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($id, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)), - $queryBuilder->expr()->eq('deleted', 0) - ) - ->executeQuery()->fetchAssociative(); + $commentRepository = GeneralUtility::makeInstance(CommentRepository::class); + return $commentRepository->findByUid($id); } + /** + * @Deprecated + */ public static function getBackendUserById(?int $userId): array|bool { if (!$userId) { return false; } - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); - - return $queryBuilder - ->select('*') - ->from('be_users') - ->where( - $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($userId, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) - ) - ->executeQuery()->fetchAssociative(); + $backendUserRepository = GeneralUtility::makeInstance(BackendUserRepository::class); + return $backendUserRepository->findByUid($userId); } + /** + * @Deprecated + */ public static function getBackendUsernameById(?int $userId): string { if (!$userId) { return ''; } - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); - - $userRecord = $queryBuilder - ->select('username', 'realName') - ->from('be_users') - ->where( - $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($userId, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) - ) - ->executeQuery()->fetchAssociative(); - - if ($userRecord) { - $user = $userRecord['username']; - if ($userRecord['realName']) { - $user = $userRecord['realName'] . ' (' . $user . ')'; - } - return htmlspecialchars($user, ENT_QUOTES, 'UTF-8'); - } - - return ''; - } - - public static function getBackendUsers(): array - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users'); - - $query = $queryBuilder - ->select('uid', 'username') - ->from('be_users') - ->orderBy('username', 'ASC'); - - return $query->executeQuery() - ->fetchAllAssociative(); - } - - public static function getExtensionRecords(string $table, ?int $pid = null): array - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); - - $query = $queryBuilder - ->select('uid', $GLOBALS['TCA'][$table]['ctrl']['label'] . ' as "title"', 'tx_ximatypo3contentplanner_status', 'tx_ximatypo3contentplanner_assignee', 'tx_ximatypo3contentplanner_comments') - ->from($table) - ->andWhere( - $queryBuilder->expr()->isNotNull('tx_ximatypo3contentplanner_status'), - $queryBuilder->expr()->eq('deleted', 0) - ) - ->orderBy('tstamp', 'DESC'); - - if ($pid) { - $query->andWhere( - $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \TYPO3\CMS\Core\Database\Connection::PARAM_INT)) - ); - } - - return $query->executeQuery() - ->fetchAllAssociative(); + $backendUserRepository = GeneralUtility::makeInstance(BackendUserRepository::class); + return $backendUserRepository->getUsernameByUid($userId); } + /** + * @Deprecated + */ public static function getExtensionRecord(?string $table, ?int $uid): array|null { if (!$table && !$uid) { @@ -232,17 +87,4 @@ public static function getExtensionRecord(?string $table, ?int $uid): array|null } return BackendUtility::getRecord($table, $uid); } - - public static function clearStatusOfExtensionRecords(string $table, int $status): void - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); - - $queryBuilder - ->update($table) - ->set('tx_ximatypo3contentplanner_status', null) - ->where( - $queryBuilder->expr()->eq('tx_ximatypo3contentplanner_status', $status) - ) - ->executeQuery(); - } } diff --git a/Classes/Utility/DiffUtility.php b/Classes/Utility/DiffUtility.php index d6420a2..8e15b98 100644 --- a/Classes/Utility/DiffUtility.php +++ b/Classes/Utility/DiffUtility.php @@ -13,64 +13,60 @@ class DiffUtility public static function timeAgo(int $timestamp): string { $now = new DateTime(); - new DateTime(); - $storedTime = new DateTime(); - $storedTime->setTimestamp($timestamp); + $storedTime = (new DateTime())->setTimestamp($timestamp); $interval = $now->diff($storedTime); - if ($interval->y > 0) { - return sprintf(self::getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf:timeAgo.' . ($interval->y == 1 ? 'year' : 'years')), $interval->y); - } - if ($interval->m > 0) { - return sprintf(self::getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf:timeAgo.' . ($interval->m == 1 ? 'month' : 'months')), $interval->m); - } - if ($interval->d > 0) { - return sprintf(self::getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf:timeAgo.' . ($interval->d == 1 ? 'day' : 'days')), $interval->d); - } - if ($interval->h > 0) { - return sprintf(self::getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf:timeAgo.' . ($interval->h == 1 ? 'hour' : 'hours')), $interval->h); - } - if ($interval->i > 0) { - return sprintf(self::getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf:timeAgo.' . ($interval->i == 1 ? 'minute' : 'minutes')), $interval->i); + $timeUnits = [ + 'y' => 'year', + 'm' => 'month', + 'd' => 'day', + 'h' => 'hour', + 'i' => 'minute', + 's' => 'second', + ]; + + foreach ($timeUnits as $unit => $label) { + if ($interval->$unit > 0) { + $key = $interval->$unit == 1 ? $label : $label . 's'; + return sprintf(self::getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf:timeAgo.' . $key), $interval->$unit); + } } - return sprintf(self::getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf:timeAgo.' . ($interval->s == 1 ? 'second' : 'seconds')), $interval->s); + + return sprintf(self::getLanguageService()->sL('LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang.xlf:timeAgo.seconds'), $interval->s); } public static function checkRecordDiff(array $data, int $actiontype): string|bool { - $old = $data['oldRecord']; - $new = $data['newRecord']; $diff = []; - foreach ($old as $key => $value) { - if ($key !== 'l10n_diffsource') { - $oldValue = $value; - $newValue = $new[$key]; - if ($oldValue !== $newValue) { - $diffValue = self::makeRecordDiffReadable($key, $actiontype, $oldValue, $newValue); - if ($diffValue) { - $diff[] = $diffValue; - } + foreach ($data['oldRecord'] as $key => $oldValue) { + if ($key === 'l10n_diffsource') { + continue; + } + + $newValue = $data['newRecord'][$key]; + if ($oldValue !== $newValue) { + $diffValue = self::makeRecordDiffReadable($key, $actiontype, $oldValue, $newValue); + if ($diffValue) { + $diff[] = $diffValue; } } } - return empty($diff) ? false : implode('
    ', $diff); + + return $diff ? implode('
    ', $diff) : false; } private static function makeRecordDiffReadable(string $field, int $actiontype, string|int|null $old, string|int|null $new): string|bool { - // set value - if (($old === null || $old === '' || $old === 0) && ($new === null || $new === '' || $new === 0)) { + if (empty($old) && empty($new)) { return false; } - if ($old === null || $old === '') { + if (empty($old)) { return sprintf(self::getLanguageService()->sL('LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang_be.xlf:history.record.' . $actiontype . '.set.' . $field), self::preparePageAttributeValue($field, $new)); } - // reset value - if ($new === null || $new === '') { + if (empty($new)) { return sprintf(self::getLanguageService()->sL('LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang_be.xlf:history.record.' . $actiontype . '.unset.' . $field), self::preparePageAttributeValue($field, $old)); } - // change value if ($old !== $new) { return sprintf(self::getLanguageService()->sL('LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang_be.xlf:history.record.' . $actiontype . '.change.' . $field), self::preparePageAttributeValue($field, $old), self::preparePageAttributeValue($field, $new)); } diff --git a/Classes/Utility/ExtensionUtility.php b/Classes/Utility/ExtensionUtility.php index 8cdb086..cd46ec0 100644 --- a/Classes/Utility/ExtensionUtility.php +++ b/Classes/Utility/ExtensionUtility.php @@ -4,7 +4,11 @@ namespace Xima\XimaTypo3ContentPlanner\Utility; +use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\PathUtility; use Xima\XimaTypo3ContentPlanner\Configuration; class ExtensionUtility @@ -36,7 +40,7 @@ public static function addContentPlannerTabToTCA(string $table): void 'label' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:pages.tx_ximatypo3contentplanner_comments', 'config' => [ 'foreign_field' => 'foreign_uid', - 'foreign_sortby' => 'sorting', + 'foreign_sortby' => 'crdate', 'foreign_table' => 'tx_ximatypo3contentplanner_comment', 'foreign_table_field' => 'foreign_table', 'type' => 'inline', @@ -92,4 +96,33 @@ public static function isRegisteredRecordTable(string $table): bool { return in_array($table, self::getRecordTables()); } + + public static function isFeatureEnabled(string $feature): bool + { + $configuration = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(Configuration::EXT_KEY); + return array_key_exists($feature, $configuration) && $configuration[$feature]; + } + + public static function getTitleField(string $table): string + { + return $GLOBALS['TCA'][$table]['ctrl']['label']; + } + + public static function getTitle(string $key, array|bool|null $record): string + { + return $record ? (array_key_exists($key, $record) ? $record[$key] : BackendUtility::getNoRecordTitle()) : BackendUtility::getNoRecordTitle(); + } + + public static function getCssTag(string $cssFileLocation, array $attributes): string + { + return sprintf( + '', + GeneralUtility::implodeAttributes([ + ...$attributes, + 'rel' => 'stylesheet', + 'media' => 'all', + 'href' => PathUtility::getPublicResourceWebPath($cssFileLocation), + ], true) + ); + } } diff --git a/Classes/Utility/IconHelper.php b/Classes/Utility/IconHelper.php new file mode 100644 index 0000000..aeaf999 --- /dev/null +++ b/Classes/Utility/IconHelper.php @@ -0,0 +1,59 @@ +getIcon($identifier, $size); + return $icon->render(); + } + + public static function getIconByStatusUid(int $uid, bool $render = false): string + { + $status = ContentUtility::getStatus($uid); + return self::getIconByStatus($status, $render); + } + + public static function getIconByStatus(?Status $status, bool $render = false, string $size = Icon::SIZE_SMALL): string + { + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $icon = $iconFactory->getIcon($status ? $status->getColoredIcon() : 'flag-gray', $size); + return $render ? $icon->render() : $icon->getIdentifier(); + } + + public static function getIconByRecord(string $table, array|bool $record, bool $render = false, string $size = Icon::SIZE_SMALL): string + { + if (!$record) { + return ''; + } + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $icon = $iconFactory->getIconForRecord($table, $record, $size); + return $render ? $icon->render() : $icon->getIdentifier(); + } + + public static function getAvatarByUserId(int $userId, int $size = 15): string + { + $user = ContentUtility::getBackendUserById($userId); + return self::getAvatarByUser($user, $size); + } + + public static function getAvatarByUser(array|bool $user, int $size = 15): string + { + if (!$user) { + return ''; + } + $avatar = GeneralUtility::makeInstance(Avatar::class); + return $avatar->render($user, $size, true); + } +} diff --git a/Classes/Utility/PermissionUtility.php b/Classes/Utility/PermissionUtility.php index 37df693..1e862f7 100644 --- a/Classes/Utility/PermissionUtility.php +++ b/Classes/Utility/PermissionUtility.php @@ -10,7 +10,7 @@ class PermissionUtility { - public static function checkAccessForRecord(string $tablename, $record): bool + public static function checkAccessForRecord(string $table, $record): bool { $backendUser = $GLOBALS['BE_USER']; if ($backendUser->user === null) { @@ -24,14 +24,14 @@ public static function checkAccessForRecord(string $tablename, $record): bool } /* @var $backendUser \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */ - if ($tablename === 'pages' && !BackendUtility::readPageAccess( + if ($table === 'pages' && !BackendUtility::readPageAccess( $record['uid'], $GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW) )) { return false; } - if (!$backendUser->check('tables_select', $tablename) || !BackendUtility::readPageAccess( + if (!$backendUser->check('tables_select', $table) || !BackendUtility::readPageAccess( $record['pid'], $GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_SHOW) )) { diff --git a/Classes/Utility/StatusRegistry.php b/Classes/Utility/StatusRegistry.php index 4b46189..ae96c84 100644 --- a/Classes/Utility/StatusRegistry.php +++ b/Classes/Utility/StatusRegistry.php @@ -25,7 +25,7 @@ public function getStatus(array &$config): void public function getStatusIcons(array &$config): void { - foreach (Configuration::STATUS_ICONS as $icon) { + foreach (Configuration\Icons::STATUS_ICONS as $icon) { $config['items'][] = [ $icon, $icon, @@ -36,7 +36,7 @@ public function getStatusIcons(array &$config): void public function getStatusColors(array &$config): void { - foreach (Configuration::STATUS_COLORS as $color) { + foreach (Configuration\Colors::STATUS_COLORS as $color) { $config['items'][] = [ $color, $color, diff --git a/Classes/Utility/UrlHelper.php b/Classes/Utility/UrlHelper.php new file mode 100644 index 0000000..87e6927 --- /dev/null +++ b/Classes/Utility/UrlHelper.php @@ -0,0 +1,68 @@ + [$table => [$uid => 'edit']], + 'returnUrl' => $generateReturnUrl ? $request->getAttribute('normalizedParams')->getRequestUri() : null, + 'columnsOnly' => 'tx_ximatypo3contentplanner_status,tx_ximatypo3contentplanner_assignee,tx_ximatypo3contentplanner_comments', + ]; + return (string)$uriBuilder->buildUriFromRoute('record_edit', $params); + } + + public static function getNewCommentUrl(string $table, int $uid): string + { + $request = $GLOBALS['TYPO3_REQUEST']; + $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); + $pid = $uid; + if ($table !== 'pages') { + $record = ContentUtility::getExtensionRecord($table, $uid); + $pid = (int)$record['pid']; + } + + $params = [ + 'edit' => ['tx_ximatypo3contentplanner_comment' => [$pid => 'new']], + 'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(), + 'defVals' => ['tx_ximatypo3contentplanner_comment' => ['foreign_table' => $table, 'foreign_uid' => $pid]], + ]; + return (string)$uriBuilder->buildUriFromRoute('record_edit', $params); + } + + public static function getRecordLink(string $table, int $uid): string + { + return match ($table) { + 'pages' => (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('web_layout', ['id' => $uid]), + default => (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('record_edit', ['edit' => [$table => [$uid => 'edit']]]), + }; + } + + public static function isRelevantWebLayoutRequest(ServerRequestInterface $request): bool + { + return $request->getAttribute('applicationType') === SystemEnvironmentBuilder::REQUESTTYPE_BE + && $request->getAttribute('module') !== null + && $request->getAttribute('module')->getIdentifier() === 'web_layout' + && in_array('tt_content', $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['registerAdditionalRecordTables']); + } + + public static function isRelevantRecordEditRequest(ServerRequestInterface $request): bool + { + return $request->getAttribute('applicationType') === SystemEnvironmentBuilder::REQUESTTYPE_BE + && ExtensionUtility::isFeatureEnabled(Configuration::FEATURE_RECORD_EDIT_HEADER_INFO) + && array_key_exists('edit', $request->getQueryParams()) + && (array_key_first($request->getQueryParams()['edit']) === 'pages' || in_array(array_key_first($request->getQueryParams()['edit']), $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['registerAdditionalRecordTables'])); + } +} diff --git a/Classes/ViewHelpers/RandomNumberViewHelper.php b/Classes/ViewHelpers/RandomNumberViewHelper.php index b1c1fed..aef6de2 100644 --- a/Classes/ViewHelpers/RandomNumberViewHelper.php +++ b/Classes/ViewHelpers/RandomNumberViewHelper.php @@ -31,6 +31,9 @@ public function initializeArguments(): void ); } + /** + * @throws \Random\RandomException + */ public function render() { return random_int($this->arguments['min'], $this->arguments['max']); diff --git a/Classes/ViewHelpers/StatusColorViewHelper.php b/Classes/ViewHelpers/StatusColorViewHelper.php new file mode 100644 index 0000000..22e4fff --- /dev/null +++ b/Classes/ViewHelpers/StatusColorViewHelper.php @@ -0,0 +1,51 @@ +registerArgument( + 'statusId', + 'integer', + '', + true + ); + $this->registerArgument( + 'colorName', + 'boolean', + '', + false, + true + ); + } + + public function render() + { + $status = $this->statusRepository->findByUid($this->arguments['statusId']); + + if (!$status) { + return ''; + } + if ($this->arguments['colorName']) { + return $status->getColor(); + } + return Configuration\Colors::get($status->getColor()); + } +} diff --git a/Classes/Widgets/ContentNotesWidget.php b/Classes/Widgets/ContentNotesWidget.php deleted file mode 100644 index 28bd8ed..0000000 --- a/Classes/Widgets/ContentNotesWidget.php +++ /dev/null @@ -1,22 +0,0 @@ -render( - 'EXT:xima_typo3_content_planner/Resources/Private/Templates/Backend/Widgets/ContentNotes.html', - [ - 'configuration' => $this->configuration, - 'records' => $this->dataProvider->getItems(), - 'options' => $this->options, - 'buttons' => $GLOBALS['BE_USER']->check('tables_modify', 'tx_ximatypo3contentplanner_note') ? $this->buttons : null, - 'icon' => 'content-thumbtack', - ] - ); - } -} diff --git a/Classes/Widgets/Provider/ContentNotesDataProvider.php b/Classes/Widgets/Provider/ContentNotesDataProvider.php deleted file mode 100644 index d7fbcb1..0000000 --- a/Classes/Widgets/Provider/ContentNotesDataProvider.php +++ /dev/null @@ -1,34 +0,0 @@ -getQueryBuilderForTable('tx_ximatypo3contentplanner_note'); - - $results = $queryBuilder - ->select( - 'uid', - 'tstamp', - 'title', - 'content' - ) - ->from('tx_ximatypo3contentplanner_note') - - ->orderBy('tstamp', 'DESC') - ->executeQuery() - ->fetchAllAssociative(); - return $results ?: []; - } -} diff --git a/Classes/Widgets/Provider/ContentStatusDataProvider.php b/Classes/Widgets/Provider/ContentStatusDataProvider.php index 20b9911..2a62a5a 100644 --- a/Classes/Widgets/Provider/ContentStatusDataProvider.php +++ b/Classes/Widgets/Provider/ContentStatusDataProvider.php @@ -6,12 +6,12 @@ use TYPO3\CMS\Dashboard\Widgets\ListDataProviderInterface; use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\BackendUserRepository; use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; class ContentStatusDataProvider implements ListDataProviderInterface { - public function __construct(protected readonly StatusRepository $statusRepository) + public function __construct(private readonly StatusRepository $statusRepository, private readonly BackendUserRepository $backendUserRepository) { } @@ -30,6 +30,6 @@ public function getStatus(): QueryResultInterface|array public function getUsers(): array { - return ContentUtility::getBackendUsers(); + return $this->backendUserRepository->findAll(); } } diff --git a/Classes/Widgets/Provider/ContentUpdateDataProvider.php b/Classes/Widgets/Provider/ContentUpdateDataProvider.php index 5c168ca..d5e865e 100644 --- a/Classes/Widgets/Provider/ContentUpdateDataProvider.php +++ b/Classes/Widgets/Provider/ContentUpdateDataProvider.php @@ -9,7 +9,6 @@ use TYPO3\CMS\Dashboard\Widgets\ListDataProviderInterface; use Xima\XimaTypo3ContentPlanner\Configuration; use Xima\XimaTypo3ContentPlanner\Domain\Model\Dto\HistoryItem; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; class ContentUpdateDataProvider implements ListDataProviderInterface { @@ -21,7 +20,7 @@ public function getItems(): array return $this->fetchUpdateData(maxItems: 15); } - public function fetchUpdateData(bool $relevanteUpdatesForCurrentUser = false, ?int $beUser = null, ?int $tstamp = null, ?int $maxItems = null, bool $cliContext = false): array + public function fetchUpdateData(?int $beUser = null, ?int $tstamp = null, ?int $maxItems = null): array { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_history'); @@ -52,14 +51,6 @@ public function fetchUpdateData(bool $relevanteUpdatesForCurrentUser = false, ?i $query->setMaxResults($maxItems*2); } - // Filter for relevant history entries, which are not created by the current user - if ($relevanteUpdatesForCurrentUser) { - $query - ->andWhere('h.userid != :userid') - ->andWhere('p.tx_ximatypo3contentplanner_assignee = :userid') - ->setParameter('userid', $beUser ?: $GLOBALS['BE_USER']->user['uid']); - } - if ($tstamp) { $query->andWhere('h.tstamp > :tstamp') ->setParameter('tstamp', $tstamp); @@ -71,21 +62,11 @@ public function fetchUpdateData(bool $relevanteUpdatesForCurrentUser = false, ?i foreach ($results as $result) { try { - $items[] = HistoryItem::create($result, $cliContext); + $items[] = HistoryItem::create($result); } catch (\Exception $e) { } } - if ($relevanteUpdatesForCurrentUser) { - $additionalItems = $this->getRecentRelevantCommentsForUser($beUser); - $items = array_merge($items, $additionalItems); - - // sort by tstamp - usort($items, function ($a, $b) { - return $b->data['tstamp'] <=> $a->data['tstamp']; - }); - } - foreach ($items as $key => $item) { if ($item->getHistoryData() === '') { unset($items[$key]); @@ -98,50 +79,4 @@ public function fetchUpdateData(bool $relevanteUpdatesForCurrentUser = false, ?i return $items; } - - /* - * It's a workaround to fetch all comments and afterwards filter them by assigned pages, because this information is serialized and cannot be filtered within the query - */ - private function getRecentRelevantCommentsForUser(?int $beUser = null): array - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_history'); - - $query = $queryBuilder - ->select( - 'h.uid', - 'h.tstamp as tstamp', - 'h.recuid as recuid', - 'h.userid as userid', - 'h.actiontype as actiontype', - 'h.tablename as tablename', - 'h.history_data as history_data', - 'b.username as username', - ) - ->from('sys_history', 'h') - ->leftJoin('h', 'be_users', 'b', 'h.userid = b.uid') - ->andWhere('h.userid != :userid') - ->andWhere('h.tablename = "tx_ximatypo3contentplanner_comment"') - ->orderBy('h.tstamp', 'DESC') - ->setParameter('userid', $beUser ?: $GLOBALS['BE_USER']->user['uid']); - - $assignedPages = ContentUtility::getAssignedPages(); - // only get uids from assigned pages - $uids = array_map(function ($page) { - return $page['uid']; - }, $assignedPages); - - $items = []; - $results = $query->executeQuery() - ->fetchAllAssociative(); - - foreach ($results as $result) { - try { - if (!is_null($result['history_data']) && in_array(json_decode($result['history_data'], true)['pid'], $uids)) { - $items[] = HistoryItem::create($result); - } - } catch (\Exception $e) { - } - } - return $items; - } } diff --git a/Classes/Widgets/Provider/CreateNoteButtonProvider.php b/Classes/Widgets/Provider/CreateNoteButtonProvider.php deleted file mode 100644 index 3c356a7..0000000 --- a/Classes/Widgets/Provider/CreateNoteButtonProvider.php +++ /dev/null @@ -1,43 +0,0 @@ -title; - } - - public function getLink(): string - { - $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); - $params = [ - 'edit' => ['tx_ximatypo3contentplanner_note' => [0 => 'new']], - 'returnUrl' => (string)$uriBuilder->buildUriFromRoute('dashboard'), - ]; - - return (string)$uriBuilder->buildUriFromRoute('record_edit', $params); - } - - public function getTarget(): string - { - return $this->target; - } - - public function getElementAttributes(): array - { - return []; - } -} diff --git a/Classes/Widgets/Provider/ListNoteButtonProvider.php b/Classes/Widgets/Provider/ListNoteButtonProvider.php deleted file mode 100644 index 156d7a9..0000000 --- a/Classes/Widgets/Provider/ListNoteButtonProvider.php +++ /dev/null @@ -1,44 +0,0 @@ -title; - } - - public function getLink(): string - { - $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); - $params = [ - 'id' => 0, - 'table' => 'tx_ximatypo3contentplanner_note', - 'returnUrl' => (string)$uriBuilder->buildUriFromRoute('dashboard'), - ]; - - return (string)$uriBuilder->buildUriFromRoute('web_list', $params); - } - - public function getTarget(): string - { - return $this->target; - } - - public function getElementAttributes(): array - { - return []; - } -} diff --git a/Classes/Widgets/Provider/StatusOverviewDataProvider.php b/Classes/Widgets/Provider/StatusOverviewDataProvider.php index 19e7d26..5495cd3 100644 --- a/Classes/Widgets/Provider/StatusOverviewDataProvider.php +++ b/Classes/Widgets/Provider/StatusOverviewDataProvider.php @@ -7,12 +7,12 @@ use TYPO3\CMS\Core\Localization\LanguageService; use TYPO3\CMS\Dashboard\Widgets\ChartDataProviderInterface; use Xima\XimaTypo3ContentPlanner\Configuration; +use Xima\XimaTypo3ContentPlanner\Domain\Repository\RecordRepository; use Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository; -use Xima\XimaTypo3ContentPlanner\Utility\ContentUtility; class StatusOverviewDataProvider implements ChartDataProviderInterface { - public function __construct(protected StatusRepository $statusRepository) + public function __construct(private readonly StatusRepository $statusRepository, private readonly RecordRepository $recordRepository) { } @@ -37,7 +37,7 @@ public function getChartData(): array public function countPageStatus(int $status = null): int { - return count(ContentUtility::getRecordsByFilter(null, $status)); + return count($this->recordRepository->findAllByFilter(status: $status)); } protected function calculateStatusCounts(): void @@ -45,7 +45,7 @@ protected function calculateStatusCounts(): void foreach ($this->statusRepository->findAll() as $status) { $this->labels[] = $status->getTitle(); $this->data[] = $this->countPageStatus($status->getUid()); - $this->colors[] = Configuration::STATUS_COLOR_CODES[$status->getColor()]; + $this->colors[] = Configuration\Colors::get($status->getColor()); } } diff --git a/Configuration/Backend/DashboardPresets.php b/Configuration/Backend/DashboardPresets.php index 92a8a13..090efc3 100644 --- a/Configuration/Backend/DashboardPresets.php +++ b/Configuration/Backend/DashboardPresets.php @@ -5,7 +5,7 @@ 'title' => 'LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang.xlf:dashboard.contentPlanner', 'description' => 'LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang.xlf:dashboard.contentPlanner.description', 'iconIdentifier' => 'flag-gray', - 'defaultWidgets' => ['contentPlanner-notes', 'contentPlanner-overview', 'contentPlanner-current', 'contentPlanner-update', 'contentPlanner-status', 'contentPlanner-comment'], + 'defaultWidgets' => ['contentPlanner-overview', 'contentPlanner-update', 'contentPlanner-current', 'contentPlanner-comment', 'contentPlanner-status'], 'showInWizard' => true, ], ]; diff --git a/Configuration/Icons.php b/Configuration/Icons.php index 60d838e..f8f07ad 100644 --- a/Configuration/Icons.php +++ b/Configuration/Icons.php @@ -3,165 +3,432 @@ declare(strict_types=1); use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider; +use TYPO3\CMS\Core\Imaging\IconProvider\SvgSpriteIconProvider; use Xima\XimaTypo3ContentPlanner\Configuration; return [ + 'actions-flag-edit' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/actions-flag-edit.svg', + ], // flag 'flag-black' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/flag-black.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-black.svg', ], 'flag-white' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/flag-white.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-white', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-white.svg', ], 'flag-toolbar' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/flag-toolbar.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-toolbar', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-toolbar.svg', ], 'flag-blue' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/flag-blue.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-blue.svg', ], 'flag-gray' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/flag-gray.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-gray.svg', ], 'flag-green' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/flag-green.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-green.svg', ], 'flag-red' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/flag-red.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-red.svg', ], 'flag-yellow' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/flag-yellow.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-yellow.svg', + ], + 'flag-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-purple.svg', + ], + 'flag-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/flags.svg#flag-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/flags/flag-orange.svg', ], // star 'star-black' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/star-black.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/stars.svg#star-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/stars/star-black.svg', ], 'star-blue' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/star-blue.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/stars.svg#star-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/stars/star-blue.svg', ], 'star-gray' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/star-gray.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/stars.svg#star-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/stars/star-gray.svg', ], 'star-green' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/star-green.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/stars.svg#star-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/stars/star-green.svg', ], 'star-red' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/star-red.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/stars.svg#star-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/stars/star-red.svg', ], 'star-yellow' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/star-yellow.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/stars.svg#star-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/stars/star-yellow.svg', + ], + 'star-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/stars.svg#star-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/stars/star-purple.svg', + ], + 'star-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/stars.svg#star-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/stars/star-orange.svg', ], // tag 'tag-black' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/tag-black.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/tags.svg#tag-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/tags/tag-black.svg', ], 'tag-blue' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/tag-blue.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/tags.svg#tag-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/tags/tag-blue.svg', ], 'tag-gray' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/tag-gray.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/tags.svg#tag-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/tags/tag-gray.svg', ], 'tag-green' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/tag-green.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/tags.svg#tag-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/tags/tag-green.svg', ], 'tag-red' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/tag-red.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/tags.svg#tag-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/tags/tag-red.svg', ], 'tag-yellow' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/tag-yellow.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/tags.svg#tag-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/tags/tag-yellow.svg', + ], + 'tag-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/tags.svg#tag-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/tags/tag-purple.svg', + ], + 'tag-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/tags.svg#tag-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/tags/tag-orange.svg', ], // info 'info-black' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/info-black.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/infos.svg#info-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/infos/info-black.svg', ], 'info-blue' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/info-blue.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/infos.svg#info-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/infos/info-blue.svg', ], 'info-gray' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/info-gray.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/infos.svg#info-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/infos/info-gray.svg', ], 'info-green' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/info-green.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/infos.svg#info-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/infos/info-green.svg', ], 'info-red' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/info-red.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/infos.svg#info-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/infos/info-red.svg', ], 'info-yellow' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/info-yellow.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/infos.svg#info-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/infos/info-yellow.svg', + ], + 'info-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/infos.svg#info-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/infos/info-purple.svg', + ], + 'info-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/infos.svg#info-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/infos/info-orange.svg', ], // heart 'heart-black' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/heart-black.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/hearts.svg#heart-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/hearts/heart-black.svg', ], 'heart-blue' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/heart-blue.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/hearts.svg#heart-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/hearts/heart-blue.svg', ], 'heart-gray' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/heart-gray.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/hearts.svg#heart-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/hearts/heart-gray.svg', ], 'heart-green' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/heart-green.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/hearts.svg#heart-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/hearts/heart-green.svg', ], 'heart-red' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/heart-red.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/hearts.svg#heart-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/hearts/heart-red.svg', ], 'heart-yellow' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/heart-yellow.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/hearts.svg#heart-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/hearts/heart-yellow.svg', + ], + 'heart-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/hearts.svg#heart-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/hearts/heart-purple.svg', + ], + 'heart-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/hearts.svg#heart-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/hearts/heart-orange.svg', + ], + // certificates + 'certificate-black' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/certificates.svg#certificate-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/certificates/certificate-black.svg', + ], + 'certificate-blue' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/certificates.svg#certificate-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/certificates/certificate-blue.svg', + ], + 'certificate-gray' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/certificates.svg#certificate-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/certificates/certificate-gray.svg', + ], + 'certificate-green' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/certificates.svg#certificate-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/certificates/certificate-green.svg', + ], + 'certificate-red' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/certificates.svg#certificate-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/certificates/certificate-red.svg', + ], + 'certificate-yellow' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/certificates.svg#certificate-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/certificates/certificate-yellow.svg', + ], + 'certificate-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/certificates.svg#certificate-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/certificates/certificate-purple.svg', + ], + 'certificate-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/certificates.svg#certificate-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/certificates/certificate-orange.svg', + ], + // exclamation + 'exclamation-black' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/exclamations.svg#exclamation-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/exclamations/exclamation-black.svg', + ], + 'exclamation-blue' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/exclamations.svg#exclamation-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/exclamations/exclamation-blue.svg', + ], + 'exclamation-gray' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/exclamations.svg#exclamation-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/exclamations/exclamation-gray.svg', + ], + 'exclamation-green' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/exclamations.svg#exclamation-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/exclamations/exclamation-green.svg', + ], + 'exclamation-red' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/exclamations.svg#exclamation-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/exclamations/exclamation-red.svg', + ], + 'exclamation-yellow' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/exclamations.svg#exclamation-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/exclamations/exclamation-yellow.svg', + ], + 'exclamation-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/exclamations.svg#exclamation-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/exclamations/exclamation-purple.svg', + ], + 'exclamation-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/exclamations.svg#exclamation-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/exclamations/exclamation-orange.svg', + ], + // rocket + 'rocket-black' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/rockets.svg#rocket-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/rockets/rocket-black.svg', + ], + 'rocket-blue' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/rockets.svg#rocket-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/rockets/rocket-blue.svg', + ], + 'rocket-gray' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/rockets.svg#rocket-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/rockets/rocket-gray.svg', + ], + 'rocket-green' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/rockets.svg#rocket-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/rockets/rocket-green.svg', + ], + 'rocket-red' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/rockets.svg#rocket-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/rockets/rocket-red.svg', + ], + 'rocket-yellow' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/rockets.svg#rocket-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/rockets/rocket-yellow.svg', + ], + 'rocket-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/rockets.svg#rocket-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/rockets/rocket-purple.svg', + ], + 'rocket-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/rockets.svg#rocket-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/rockets/rocket-orange.svg', + ], + // thumbtack + 'thumbtack-black' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/thumbtacks.svg#thumbtack-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/thumbtacks/thumbtack-black.svg', + ], + 'thumbtack-blue' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/thumbtacks.svg#thumbtack-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/thumbtacks/thumbtack-blue.svg', + ], + 'thumbtack-gray' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/thumbtacks.svg#thumbtack-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/thumbtacks/thumbtack-gray.svg', + ], + 'thumbtack-green' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/thumbtacks.svg#thumbtack-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/thumbtacks/thumbtack-green.svg', + ], + 'thumbtack-red' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/thumbtacks.svg#thumbtack-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/thumbtacks/thumbtack-red.svg', + ], + 'thumbtack-yellow' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/thumbtacks.svg#thumbtack-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/thumbtacks/thumbtack-yellow.svg', + ], + 'thumbtack-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/thumbtacks.svg#thumbtack-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/thumbtacks/thumbtack-purple.svg', + ], + 'thumbtack-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/thumbtacks.svg#thumbtack-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/thumbtacks/thumbtack-orange.svg', ], // color 'color-black' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/color-black.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/colors.svg#color-black', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/colors/color-black.svg', ], 'color-blue' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/color-blue.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/colors.svg#color-blue', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/colors/color-blue.svg', ], 'color-gray' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/color-gray.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/colors.svg#color-gray', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/colors/color-gray.svg', ], 'color-green' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/color-green.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/colors.svg#color-green', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/colors/color-green.svg', ], 'color-red' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/color-red.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/colors.svg#color-red', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/colors/color-red.svg', ], 'color-yellow' => [ - 'provider' => SvgIconProvider::class, - 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/color-yellow.svg', + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/colors.svg#color-yellow', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/colors/color-yellow.svg', + ], + 'color-purple' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/colors.svg#color-purple', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/colors/color-purple.svg', + ], + 'color-orange' => [ + 'provider' => SvgSpriteIconProvider::class, + 'sprite' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/sprites/colors.svg#color-orange', + 'source' => 'EXT:' . Configuration::EXT_KEY . '/Resources/Public/Icons/svgs/colors/color-orange.svg', ], ]; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 8043c1c..f7ef18c 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -8,14 +8,20 @@ services: resource: '../Classes/*' exclude: '../Classes/Domain/Model/*' - cache.ximatypo3contentplanner_toolbarcache: + cache.ximatypo3contentplanner_statuscache: class: TYPO3\CMS\Core\Cache\Frontend\FrontendInterface factory: [ '@TYPO3\CMS\Core\Cache\CacheManager', 'getCache' ] - arguments: [ 'ximatypo3contentplanner_toolbarcache' ] + arguments: [ 'ximatypo3contentplanner_statuscache' ] - Xima\XimaTypo3ContentPlanner\Backend\ToolbarItems\UpdateItem: + Xima\XimaTypo3ContentPlanner\Domain\Repository\StatusRepository: arguments: - $cache: '@cache.ximatypo3contentplanner_toolbarcache' + $cache: '@cache.ximatypo3contentplanner_statuscache' + + Xima\XimaTypo3ContentPlanner\Hooks\DataHandlerHook: + public: true + arguments: + $cache: '@cache.ximatypo3contentplanner_statuscache' + $statusChangeManager: '@Xima\XimaTypo3ContentPlanner\Manager\StatusChangeManager' Xima\XimaTypo3ContentPlanner\EventListener\DrawBackendHeaderListener: tags: @@ -42,12 +48,6 @@ services: - name: event.listener identifier: 'xima-typo3-content-planner/backend/modify-record-list-record-actions' - Xima\XimaTypo3ContentPlanner\Command\NotifyUpdateCommand: - tags: - - name: console.command - command: 'content-planner:notifiy-update' - description: 'A command to notify users about relevant updates in the content planner.' - Xima\XimaTypo3ContentPlanner\Command\BulkUpdateCommand: tags: - name: console.command @@ -102,8 +102,8 @@ services: title: 'LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang.xlf:widgets.contentPlanner.comment.title' description: 'LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang.xlf:widgets.contentPlanner.comment.description' iconIdentifier: 'content-message' - height: 'large' - width: 'medium' + height: 'medium' + width: 'small' dashboard.widget.ContentStatus-overview: class: 'TYPO3\CMS\Dashboard\Widgets\DoughnutChartWidget' @@ -120,30 +120,6 @@ services: iconIdentifier: 'content-widget-chart-pie' height: 'medium' - Xima\XimaTypo3ContentPlanner\Widgets\Provider\CreateNoteButtonProvider: - arguments: - $title: 'LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang.xlf:widgets.contentPlanner.notes.button.create' - - Xima\XimaTypo3ContentPlanner\Widgets\Provider\ListNoteButtonProvider: - arguments: - $title: 'LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang.xlf:widgets.contentPlanner.notes.button.list' - - dashboard.widget.ContentStatus-notes: - class: 'Xima\XimaTypo3ContentPlanner\Widgets\ContentNotesWidget' - arguments: - $dataProvider: '@Xima\XimaTypo3ContentPlanner\Widgets\Provider\ContentNotesDataProvider' - $buttons: - - '@Xima\XimaTypo3ContentPlanner\Widgets\Provider\CreateNoteButtonProvider' - - '@Xima\XimaTypo3ContentPlanner\Widgets\Provider\ListNoteButtonProvider' - tags: - - name: dashboard.widget - identifier: 'contentPlanner-notes' - groupNames: 'contentPlanner' - title: 'LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang.xlf:widgets.contentPlanner.notes.title' - description: 'LLL:EXT:xima_typo3_content_planner/Resources/Private/Language/locallang.xlf:widgets.contentPlanner.notes.description' - iconIdentifier: 'content-thumbtack' - height: 'medium' - dashboard.widget.ContentStatus-update: class: 'Xima\XimaTypo3ContentPlanner\Widgets\ContentUpdateWidget' arguments: diff --git a/Configuration/TCA/Overrides/be_users.php b/Configuration/TCA/Overrides/be_users.php index dff27df..0775ef6 100644 --- a/Configuration/TCA/Overrides/be_users.php +++ b/Configuration/TCA/Overrides/be_users.php @@ -21,23 +21,9 @@ ], ], ], - 'tx_ximatypo3contentplanner_subscribe' => [ - 'exclude' => 1, - 'label' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:be_users.tx_ximatypo3contentplanner_subscribe', - 'description' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:be_users.tx_ximatypo3contentplanner_subscribe.description', - 'config' => [ - 'type' => 'select', - 'renderType' => 'selectSingle', - 'items' => [ - ['label' => 'Never', 'value' => ''], - ['label' => 'Daily', 'value' => 'daily'], - ['label' => 'Weekly', 'value' => 'weekly'], - ], - ], - ], ]; $GLOBALS['TCA']['pages']['palettes']['tx_ximatypo3contentplanner'] = [ - 'showitem' => 'tx_ximatypo3contentplanner_hide, tx_ximatypo3contentplanner_subscribe', + 'showitem' => 'tx_ximatypo3contentplanner_hide', ]; ExtensionManagementUtility::addTCAcolumns('be_users', $temporaryColumns); diff --git a/Configuration/TCA/tx_ximatypo3contentplanner_comment.php b/Configuration/TCA/tx_ximatypo3contentplanner_comment.php index 0484acc..72a7857 100644 --- a/Configuration/TCA/tx_ximatypo3contentplanner_comment.php +++ b/Configuration/TCA/tx_ximatypo3contentplanner_comment.php @@ -16,6 +16,7 @@ 'typeicon_classes' => [ 'default' => 'content-message', ], + 'default_sortby' => 'crdate', 'searchFields' => 'title,text', 'hideTable' => true, 'security' => [ @@ -35,11 +36,6 @@ 'type' => 'check', ], ], - 'sorting' => [ - 'config' => [ - 'type' => 'passthrough', - ], - ], 'content' => [ 'exclude' => 1, 'label' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:tx_ximatypo3contentplanner_comment.content', diff --git a/Configuration/TCA/tx_ximatypo3contentplanner_note.php b/Configuration/TCA/tx_ximatypo3contentplanner_note.php deleted file mode 100644 index 29232a3..0000000 --- a/Configuration/TCA/tx_ximatypo3contentplanner_note.php +++ /dev/null @@ -1,58 +0,0 @@ - [ - 'title' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_be.xlf:note', - 'label' => 'title', - 'tstamp' => 'tstamp', - 'crdate' => 'crdate', - 'adminOnly' => true, - 'rootLevel' => 1, - 'delete' => 'deleted', - 'enablecolumns' => [ - 'disabled' => 'hidden', - ], - 'default_sortby' => 'crdate DESC', - 'typeicon_classes' => [ - 'default' => 'mimetypes-x-sys_news', - ], - 'searchFields' => 'title,content', - ], - 'types' => [ - '0' => [ - 'showitem' => 'title,content', - ], - ], - 'columns' => [ - 'hidden' => [ - 'exclude' => 1, - 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.hidden', - 'config' => [ - 'type' => 'check', - ], - ], - 'title' => [ - 'exclude' => 1, - 'label' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:tx_ximatypo3contentplanner_note.title', - 'config' => [ - 'type' => 'input', - 'eval' => 'trim', - 'required' => true, - ], - ], - 'content' => [ - 'exclude' => 1, - 'label' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:tx_ximatypo3contentplanner_note.text', - 'config' => [ - 'enableRichtext' => true, - 'type' => 'text', - 'eval' => 'trim', - 'cols' => 48, - 'rows' => 5, - 'required' => true, - ], - ], - ], -]; diff --git a/Documentation/Images/categories.png b/Documentation/Images/categories.png new file mode 100644 index 0000000..4f43114 Binary files /dev/null and b/Documentation/Images/categories.png differ diff --git a/Documentation/Images/dashboard.png b/Documentation/Images/dashboard.png index 0b9a22c..7dd011b 100644 Binary files a/Documentation/Images/dashboard.png and b/Documentation/Images/dashboard.png differ diff --git a/Documentation/Images/page.png b/Documentation/Images/page.png index 834bf59..4a044df 100644 Binary files a/Documentation/Images/page.png and b/Documentation/Images/page.png differ diff --git a/Documentation/Images/screencast.gif b/Documentation/Images/screencast.gif new file mode 100644 index 0000000..1e0de16 Binary files /dev/null and b/Documentation/Images/screencast.gif differ diff --git a/Documentation/Images/tt_content.png b/Documentation/Images/tt_content.png new file mode 100644 index 0000000..015ef59 Binary files /dev/null and b/Documentation/Images/tt_content.png differ diff --git a/README.md b/README.md index c5dc59d..d43f998 100644 --- a/README.md +++ b/README.md @@ -50,14 +50,14 @@ Download the zip file from [TYPO3 extension repository (TER)](https://extensions ### Status -By default they are four different default status available: +By default, they are four different default status available: - *Pending*: The page is not yet ready for editing. - *In progress*: The page is currently being edited. - *Needs review*: The page is ready for review. - *Completed*: The page is ready to be published. -> **Hint**: The status are content generated on the root page. Add/remove/adjust them to fit your needs. +> **Hint**: The status are content generated on the root page. Add/remove/adjust them to fit your needs regarding custom title, predefined color and icon. Change the page status easily: @@ -65,13 +65,17 @@ Change the page status easily: - In the page tree context menu - In the page backend header +### Assignee and comments + Assign a user to the page to distribute the content work. >our own assignment is highlighted in the dashboard. > **Hint**: By default the auto assignee feature is enabled. The assignee is automatically set to the current user when the status is changed from stateless to a new state. -Add some helpful comments within the page to support the content work. +Configure the auto assignee feature and more in the __extension settings__. + +Use the comment feature to add some helpful message within the records to support the content work. -![Screencast](./Documentation/Images/screencast-content-planner.gif) +![Screencast](./Documentation/Images/screencast.gif) ### Dashboard @@ -83,13 +87,27 @@ Add custom notes to the dashboard to influence the content planning. ## Configuration -Feature toggles are available, see `ext_localconf.php` for configuration options. +Feature toggles are available in the __extension settings__. + +### User settings The content planner abilities are part of a **custom permission** and needed to be granted to the dedicated user group/s (except admins). Every user can easily disable the content planner features in the user settings to avoid colour overload. -## Additional record tables +## Command + +Use the bulk update command to process multiple entities at once. See help for more information regarding the specific usage. + +```bash +vendor/bin/typo3 content-planner:bulk-update --help +``` + +## Extend + +### Additional record tables + +![Categories](./Documentation/Images/categories.png) If you want to extend the content planner to other record tables (e.g. news), follow the steps below: @@ -116,6 +134,25 @@ CREATE TABLE tx_news_domain_model_news $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['xima_typo3_content_planner']['registerAdditionalRecordTables'][] = 'tx_news_domain_model_news'; ``` +> **Hint**: The extension also support the content status functionality for content elements as well. + +![Content](./Documentation/Images/tt_content.png) + +### Events + +This extension provides several events to hook into the content planner functionality. + +- [StatusChangeEvent](Classes/Event/StatusChangeEvent.php) +- [PrepareStatusSelectionEvent.php](Classes/Event/PrepareStatusSelectionEvent.php) + +## Development + +Use the following ddev command to easily install all support TYPO3 versions. + +```bash +ddev install all +``` + ## License This project is licensed diff --git a/Resources/Private/Language/de.locallang.xlf b/Resources/Private/Language/de.locallang.xlf new file mode 100644 index 0000000..497611e --- /dev/null +++ b/Resources/Private/Language/de.locallang.xlf @@ -0,0 +1,171 @@ + + + +
    + + + + Content Planner + + + + Übersichtsdashboard für Inhaltsstatusinformationen + + + + Content Planner + + + + Status + + + + Alle Datensätze mit Inhaltsstatus anzeigen + + + + Aktueller Zuständigkeit + + + + Alle Datensätze mit Ihrer Zuweisung anzeigen + + + + Übersicht + + + + Ein Diagramm mit der Verteilung des Inhaltsstatus anzeigen + + + + Neueste Kommentare + + + + Die neuesten Inhaltskommentare anzeigen + + + + Neueste Updates + + + + Die neuesten Updates hinsichtlich Inhaltsstatusänderungen anzeigen + + + + Status + + + + Datensatz + + + + Seite + + + + Geändert + + + + Zuständigkeit + + + + Autor + + + + Kommentar + + + + Kommentare + + + + Seite + + + + Benutzer + + + + Änderungshinweis + + + + + Kommentare + + + + Abbrechen + + + + Neu + + + + Speichern + + + + + + vor %s Jahren + + + + vor %s Jahr + + + + vor %s Monaten + + + + vor %s Monat + + + + vor %s Tagen + + + + vor %s Tag + + + + vor %s Stunden + + + + vor %s Stunde + + + + vor %s Minuten + + + + vor %s Minute + + + + vor %s Sekunden + + + + vor %s Sekunde + + + + diff --git a/Resources/Private/Language/de.locallang_be.xlf b/Resources/Private/Language/de.locallang_be.xlf new file mode 100644 index 0000000..fe118a2 --- /dev/null +++ b/Resources/Private/Language/de.locallang_be.xlf @@ -0,0 +1,85 @@ + + + +
    + + + + Inhaltsstatus + + + + Ausstehend + + + + In Bearbeitung + + + + Benötigt Überprüfung + + + + Abgeschlossen + + + + Status zurücksetzen + + + + Content Planner Kommentar + + + + Kommentare + + + + Aktueller Zuständiger + + + + Speichern und Schließen + + + + + Status wurde auf %s gesetzt]]> + + + + Status wurde von %s entfernt]]> + + + + Status wurde von %s zu %s geändert]]> + + + + Zuständigkeit wurde auf %s gesetzt]]> + + + + Zuständigkeit wurde von %s entfernt]]> + + + + Zuständigkeit wurde von %s zu %s geändert]]> + + + + Kommentar wurde hinzugefügt]]> + + + + Kommentar wurde aktualisiert]]> + + + + Kommentar wurde entfernt]]> + + + + diff --git a/Resources/Private/Language/de.locallang_db.xlf b/Resources/Private/Language/de.locallang_db.xlf new file mode 100644 index 0000000..ce1f0ae --- /dev/null +++ b/Resources/Private/Language/de.locallang_db.xlf @@ -0,0 +1,64 @@ + + + +
    + + + + Status + + + + Kommentare + + + + Zuständigkeit + + + + Inhalt + + + + Autor + + + + Content Planner ausblenden + + + + Deaktivieren Sie die Anzeige von Content Planner Statusinformationen und Farben im TYPO3-Backend + + + + Titel + + + + Symbol + + + + Farbe + + + + Content Planner + + + + Inhaltsstatus + + + + Erlauben Sie die Verwendung des Inhaltsstatus innerhalb von Datensätzen + + + + Content Planner + + + + diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 3e2db05..892018f 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -13,43 +13,31 @@ Content Planner - Content Planner Status + Status - Display all pages with content status + Display all records with content status - Content Planner Current Assignee + Current Assignee - Display all pages with your assignment + Display all records with your assignment - Content Planner Overview + Overview Display a donut chart with the distribution of content status - Content Planner Recent Comments + Recent Comments Display the recent content comments - - Content Planner Notes - - - Display the content status notes - - - New - - - List - - Content Planner Recent Updates + Recent Updates Display the recent updates according to content status changes @@ -88,9 +76,15 @@ Change note + + Comments + Cancel + + New + Save diff --git a/Resources/Private/Language/locallang_be.xlf b/Resources/Private/Language/locallang_be.xlf index 8eee0dd..8c56893 100644 --- a/Resources/Private/Language/locallang_be.xlf +++ b/Resources/Private/Language/locallang_be.xlf @@ -24,8 +24,14 @@ Content Planner Comment - - Content Planner Note + + Comments + + + Current Assignee + + + Save and Close diff --git a/Resources/Private/Language/locallang_db.xlf b/Resources/Private/Language/locallang_db.xlf index 27620ce..ddb2b58 100644 --- a/Resources/Private/Language/locallang_db.xlf +++ b/Resources/Private/Language/locallang_db.xlf @@ -24,18 +24,6 @@ Disable the display of content planner status information and color within the TYPO3 backend - - Subscribe for updates - - - Subscribe to e-mail notification for content planner updates - - - Title - - - Text - Title diff --git a/Resources/Private/Partials/Empty.html b/Resources/Private/Partials/Empty.html index d130bfb..a7ee532 100644 --- a/Resources/Private/Partials/Empty.html +++ b/Resources/Private/Partials/Empty.html @@ -5,3 +5,17 @@ EXT:xima_typo3_content_planner/Resources/Public/Icons/icon-relax-{xcp:randomNumber(min: 1,max: 5)}.svg
    + + diff --git a/Resources/Private/Templates/Backend/Header/HeaderInfo.html b/Resources/Private/Templates/Backend/Header/HeaderInfo.html new file mode 100644 index 0000000..d75cb89 --- /dev/null +++ b/Resources/Private/Templates/Backend/Header/HeaderInfo.html @@ -0,0 +1,107 @@ + + +
    +
    + +
    +
    + + + + Status:  {status.title} + + + + Status:  {status.title} + + + +
    +
    +
    + + + ● + + +
    + +
    + +
    +
    + + + + + + + + + + + +
    +
    + diff --git a/Resources/Private/Templates/Backend/PageHeader/PageHeaderInfo.html b/Resources/Private/Templates/Backend/PageHeader/PageHeaderInfo.html deleted file mode 100644 index d0f452b..0000000 --- a/Resources/Private/Templates/Backend/PageHeader/PageHeaderInfo.html +++ /dev/null @@ -1,58 +0,0 @@ - - -
    -
    -
    -
    - - - -
    -
    -
    - - Content Planner Status: - {status.title} - - -
    - -
    - - {assignee} -
    -
    - - - - - - New - - - - Edit - -
    -
    -
    -
    -
    -
    diff --git a/Resources/Private/Templates/Backend/ToolbarItems/UpdateItem.html b/Resources/Private/Templates/Backend/ToolbarItems/UpdateItem.html deleted file mode 100644 index e157763..0000000 --- a/Resources/Private/Templates/Backend/ToolbarItems/UpdateItem.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - -Content Planner Relevant Updates - - {count} - diff --git a/Resources/Private/Templates/Backend/ToolbarItems/UpdateItemDropDown.html b/Resources/Private/Templates/Backend/ToolbarItems/UpdateItemDropDown.html deleted file mode 100644 index 346733a..0000000 --- a/Resources/Private/Templates/Backend/ToolbarItems/UpdateItemDropDown.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - diff --git a/Resources/Private/Templates/Backend/Widgets/ContentCommentList.html b/Resources/Private/Templates/Backend/Widgets/ContentCommentList.html index 5250cc2..4e4c4f5 100644 --- a/Resources/Private/Templates/Backend/Widgets/ContentCommentList.html +++ b/Resources/Private/Templates/Backend/Widgets/ContentCommentList.html @@ -11,26 +11,29 @@
    - - -
    - -
    -
    {record.authorName} {record.data.tstamp -> f:format.date(format:'d.m.Y - H:i')}
    -
    {record.data.content -> f:sanitize.html()}
    + + +
    + +
    +
    +
    + {record.authorName} + {record.data.tstamp -> f:format.date(format:'d.m.Y H:i')} +
    + + - - - - {record.title} - + {record.title} -
    +
    - - +
    {record.data.content -> f:sanitize.html()}
    +
    +
    + +
    diff --git a/Resources/Private/Templates/Backend/Widgets/ContentNotes.html b/Resources/Private/Templates/Backend/Widgets/ContentNotes.html index dc7ec9b..dc06a15 100644 --- a/Resources/Private/Templates/Backend/Widgets/ContentNotes.html +++ b/Resources/Private/Templates/Backend/Widgets/ContentNotes.html @@ -15,7 +15,7 @@
    - +
    diff --git a/Resources/Private/Templates/Backend/Widgets/ContentStatusList.html b/Resources/Private/Templates/Backend/Widgets/ContentStatusList.html index 09fe350..6077098 100644 --- a/Resources/Private/Templates/Backend/Widgets/ContentStatusList.html +++ b/Resources/Private/Templates/Backend/Widgets/ContentStatusList.html @@ -88,7 +88,7 @@
    - +
    diff --git a/Resources/Private/Templates/Default/Comments.html b/Resources/Private/Templates/Default/Comments.html index 4201d75..b16e0c6 100644 --- a/Resources/Private/Templates/Default/Comments.html +++ b/Resources/Private/Templates/Default/Comments.html @@ -2,42 +2,62 @@ xmlns:be="http://typo3.org/ns/TYPO3/CMS/Backend/ViewHelpers" data-namespace-typo3-fluid="true"> - + + + + - - -
    +
    -
    {record.authorName} {record.data.tstamp -> f:format.date(format:'d.m.Y - H:i')}
    -
    {record.data.content -> f:sanitize.html()}
    - + +
    {record.data.content -> f:sanitize.html()}
    diff --git a/Resources/Private/Templates/Email/ContentUpdates.html b/Resources/Private/Templates/Email/ContentUpdates.html deleted file mode 100644 index 5695c0f..0000000 --- a/Resources/Private/Templates/Email/ContentUpdates.html +++ /dev/null @@ -1,215 +0,0 @@ - - - - - - - - - - - - - - diff --git a/Resources/Public/Css/Header.css b/Resources/Public/Css/Header.css new file mode 100644 index 0000000..b424bdb --- /dev/null +++ b/Resources/Public/Css/Header.css @@ -0,0 +1,119 @@ +:root { + --content-planner--color-black: rgb(144,164,174); + --content-planner--color-red: rgb(250,136,147); + --content-planner--color-yellow: rgb(255,205,117); + --content-planner--color-green: rgb(106,158,113); + --content-planner--color-blue: rgb(100,187,200); + --content-planner--color-purple: rgb(92,107,192); + --content-planner--color-orange: rgb(255,112,67); + + --content-planner--color-black-light: rgb(144,164,174, 0.2); + --content-planner--color-red-light: rgb(250,136,147, 0.2); + --content-planner--color-yellow-light: rgb(255,205,117, 0.2); + --content-planner--color-green-light: rgb(106,158,113, 0.2); + --content-planner--color-blue-light: rgb(100,187,200, 0.2); + --content-planner--color-purple-light: rgb(92,107,192, 0.2); + --content-planner--color-orange-light: rgb(255,112,67, 0.2); +} + +.xima-typo3-content-planner--header-info { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 1em; + margin-bottom: 1em; + padding: 1em; + border-radius: 5px; + border-top: 12px solid; +} + +.xima-typo3-content-planner--header-info .left { + margin: 0 12px; +} + +.xima-typo3-content-planner--header-info .body { + flex-grow: 1; +} + +.xima-typo3-content-planner--header-info[data-color="black"] { + background-color: var(--content-planner--color-black-light); + border-color: var(--content-planner--color-black); +} + +.xima-typo3-content-planner--header-info[data-color="red"] { + background-color: var(--content-planner--color-red-light); + border-color: var(--content-planner--color-red); +} + +.xima-typo3-content-planner--header-info[data-color="yellow"] { + background-color: var(--content-planner--color-yellow-light); + border-color: var(--content-planner--color-yellow); +} + +.xima-typo3-content-planner--header-info[data-color="green"] { + background-color: var(--content-planner--color-green-light); + border-color: var(--content-planner--color-green); +} + +.xima-typo3-content-planner--header-info[data-color="blue"] { + background-color: var(--content-planner--color-blue-light); + border-color: var(--content-planner--color-blue); +} + +.xima-typo3-content-planner--header-info[data-color="purple"] { + background-color: var(--content-planner--color-purple-light); + border-color: var(--content-planner--color-purple); +} + +.xima-typo3-content-planner--header-info[data-color="orange"] { + background-color: var(--content-planner--color-orange-light); + border-color: var(--content-planner--color-orange); +} + +.xima-typo3-content-planner--header--ce-hint { + display: flex; + align-items: center; + margin-right: 1em; +} + +.xima-typo3-content-planner--header--ce-hint a { + padding: 0 0.5em; +} + +.xima-typo3-content-planner--header--ce-hint a:hover { + text-decoration: none; + transform: scale(1.4); +} + +.xima-typo3-content-planner--header--ce-hint a[data-color="black"] { + color: var(--content-planner--color-black); +} + +.xima-typo3-content-planner--header--ce-hint a[data-color="red"] { + color: var(--content-planner--color-red); +} + +.xima-typo3-content-planner--header--ce-hint a[data-color="yellow"] { + color: var(--content-planner--color-yellow); +} + +.xima-typo3-content-planner--header--ce-hint a[data-color="green"] { + color: var(--content-planner--color-green); +} + +.xima-typo3-content-planner--header--ce-hint a[data-color="blue"] { + color: var(--content-planner--color-blue); +} + +.xima-typo3-content-planner--header--ce-hint a[data-color="purple"] { + color: var(--content-planner--color-purple); +} + +.xima-typo3-content-planner--header--ce-hint a[data-color="orange"] { + color: var(--content-planner--color-orange); +} + +.current { + background-color: rgba(253, 212, 150, 0.2); +} diff --git a/Resources/Public/Css/Widgets.css b/Resources/Public/Css/Widgets.css index 880b8cf..1de830c 100644 --- a/Resources/Public/Css/Widgets.css +++ b/Resources/Public/Css/Widgets.css @@ -5,7 +5,6 @@ .widget-content-main .widget-loading { width: 100%; height: 100%; - background-color: #ffffff; display: flex; align-items: center; justify-content: center; @@ -30,11 +29,6 @@ opacity: 0.125; background-position: 50% 55%; background-size: 25%; -" -} - -.widget-content-main table tr.current { - background-color: #FFF3E0; } .widget-content-main .avatar { @@ -43,7 +37,7 @@ } .widget-content-main .widget-filter + .widget-table-wrapper { - padding-top: 4rem; + padding-top: 4.5rem; height: 100%; } @@ -59,7 +53,7 @@ margin-left: -20px; padding-left: 20px; padding-top: 5px; - background: #fff; + background-color: var(--typo3-component-bg); } .widget-content-main .widget-filter .form-check input[type="radio"] { @@ -88,17 +82,15 @@ } .widget-contentPlanner--comment { - border-bottom: 1px solid #f9f9f9; padding-bottom: 1rem; gap: 1rem; - margin-bottom: 2rem; } .widget-contentPlanner--comment .text { position: relative; display: inline-block; padding: 1rem; - background-color: #f9f9f9; + background-color: rgba(125, 125, 125, 0.1); margin: 1rem 0; } @@ -117,15 +109,11 @@ } .update-list li { - border-bottom: 1px solid #e0e0e0; -} - -.update-list li.current { - background-color: #FFF3E0; + border-bottom: 1px solid rgba(0, 0, 0, 0.2); } .update-list li .change-type-icon { - background-color: #eee; + border-bottom: 1px solid rgba(0, 0, 0, 0.4); padding: 20px; margin: 0 10px; z-index: 2; @@ -137,5 +125,9 @@ padding-left: 40px; height: 100%; display: block; - border-left: 1px solid #ddd; + border-left: 1px solid rgba(0, 0, 0, 0.2); +} + +.widget-content-main table tr.current, .update-list li.current { + background-color: rgba(253, 212, 150, 0.2); } diff --git a/Resources/Public/Icons/actions-flag-edit.svg b/Resources/Public/Icons/actions-flag-edit.svg new file mode 100644 index 0000000..dfffbd3 --- /dev/null +++ b/Resources/Public/Icons/actions-flag-edit.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Resources/Public/Icons/icon-relax-1.svg b/Resources/Public/Icons/icon-relax-1.svg index d6fa998..21c1d8a 100644 --- a/Resources/Public/Icons/icon-relax-1.svg +++ b/Resources/Public/Icons/icon-relax-1.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/icon-relax-2.svg b/Resources/Public/Icons/icon-relax-2.svg index f0c1926..c8acf60 100644 --- a/Resources/Public/Icons/icon-relax-2.svg +++ b/Resources/Public/Icons/icon-relax-2.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/icon-relax-3.svg b/Resources/Public/Icons/icon-relax-3.svg index a1a0d0a..03934e2 100644 --- a/Resources/Public/Icons/icon-relax-3.svg +++ b/Resources/Public/Icons/icon-relax-3.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/icon-relax-4.svg b/Resources/Public/Icons/icon-relax-4.svg index 9905939..c22eba5 100644 --- a/Resources/Public/Icons/icon-relax-4.svg +++ b/Resources/Public/Icons/icon-relax-4.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/icon-relax-5.svg b/Resources/Public/Icons/icon-relax-5.svg index ec70b13..aa7e459 100644 --- a/Resources/Public/Icons/icon-relax-5.svg +++ b/Resources/Public/Icons/icon-relax-5.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/info-black.svg b/Resources/Public/Icons/info-black.svg deleted file mode 100644 index f5e8bac..0000000 --- a/Resources/Public/Icons/info-black.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Resources/Public/Icons/info-blue.svg b/Resources/Public/Icons/info-blue.svg deleted file mode 100644 index 02b0a10..0000000 --- a/Resources/Public/Icons/info-blue.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Resources/Public/Icons/info-green.svg b/Resources/Public/Icons/info-green.svg deleted file mode 100644 index da75d77..0000000 --- a/Resources/Public/Icons/info-green.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Resources/Public/Icons/info-red.svg b/Resources/Public/Icons/info-red.svg deleted file mode 100644 index 1e4f398..0000000 --- a/Resources/Public/Icons/info-red.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Resources/Public/Icons/info-yellow.svg b/Resources/Public/Icons/info-yellow.svg deleted file mode 100644 index fcbda9a..0000000 --- a/Resources/Public/Icons/info-yellow.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Resources/Public/Icons/sprites/certificates.svg b/Resources/Public/Icons/sprites/certificates.svg new file mode 100644 index 0000000..043d540 --- /dev/null +++ b/Resources/Public/Icons/sprites/certificates.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/colors.svg b/Resources/Public/Icons/sprites/colors.svg new file mode 100644 index 0000000..e7cab3f --- /dev/null +++ b/Resources/Public/Icons/sprites/colors.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/exclamations.svg b/Resources/Public/Icons/sprites/exclamations.svg new file mode 100644 index 0000000..9a483d5 --- /dev/null +++ b/Resources/Public/Icons/sprites/exclamations.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/flags.svg b/Resources/Public/Icons/sprites/flags.svg new file mode 100644 index 0000000..28ac765 --- /dev/null +++ b/Resources/Public/Icons/sprites/flags.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/hearts.svg b/Resources/Public/Icons/sprites/hearts.svg new file mode 100644 index 0000000..071442f --- /dev/null +++ b/Resources/Public/Icons/sprites/hearts.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/infos.svg b/Resources/Public/Icons/sprites/infos.svg new file mode 100644 index 0000000..6eff80e --- /dev/null +++ b/Resources/Public/Icons/sprites/infos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/rockets.svg b/Resources/Public/Icons/sprites/rockets.svg new file mode 100644 index 0000000..9addb08 --- /dev/null +++ b/Resources/Public/Icons/sprites/rockets.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/stars.svg b/Resources/Public/Icons/sprites/stars.svg new file mode 100644 index 0000000..ae7d182 --- /dev/null +++ b/Resources/Public/Icons/sprites/stars.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/tags.svg b/Resources/Public/Icons/sprites/tags.svg new file mode 100644 index 0000000..38d7741 --- /dev/null +++ b/Resources/Public/Icons/sprites/tags.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/sprites/thumbtacks.svg b/Resources/Public/Icons/sprites/thumbtacks.svg new file mode 100644 index 0000000..e1db621 --- /dev/null +++ b/Resources/Public/Icons/sprites/thumbtacks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/svgs/certificates/certificate-black.svg b/Resources/Public/Icons/svgs/certificates/certificate-black.svg new file mode 100644 index 0000000..3d4d45d --- /dev/null +++ b/Resources/Public/Icons/svgs/certificates/certificate-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/svgs/certificates/certificate-blue.svg b/Resources/Public/Icons/svgs/certificates/certificate-blue.svg new file mode 100644 index 0000000..daeb948 --- /dev/null +++ b/Resources/Public/Icons/svgs/certificates/certificate-blue.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/certificates/certificate-green.svg b/Resources/Public/Icons/svgs/certificates/certificate-green.svg new file mode 100644 index 0000000..7d785db --- /dev/null +++ b/Resources/Public/Icons/svgs/certificates/certificate-green.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/certificates/certificate-orange.svg b/Resources/Public/Icons/svgs/certificates/certificate-orange.svg new file mode 100644 index 0000000..a3f90a1 --- /dev/null +++ b/Resources/Public/Icons/svgs/certificates/certificate-orange.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/certificates/certificate-purple.svg b/Resources/Public/Icons/svgs/certificates/certificate-purple.svg new file mode 100644 index 0000000..7de1706 --- /dev/null +++ b/Resources/Public/Icons/svgs/certificates/certificate-purple.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/certificates/certificate-red.svg b/Resources/Public/Icons/svgs/certificates/certificate-red.svg new file mode 100644 index 0000000..c884b7e --- /dev/null +++ b/Resources/Public/Icons/svgs/certificates/certificate-red.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/certificates/certificate-yellow.svg b/Resources/Public/Icons/svgs/certificates/certificate-yellow.svg new file mode 100644 index 0000000..9d7490a --- /dev/null +++ b/Resources/Public/Icons/svgs/certificates/certificate-yellow.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/color-black.svg b/Resources/Public/Icons/svgs/colors/color-black.svg similarity index 74% rename from Resources/Public/Icons/color-black.svg rename to Resources/Public/Icons/svgs/colors/color-black.svg index 6baf3f5..82c55a7 100644 --- a/Resources/Public/Icons/color-black.svg +++ b/Resources/Public/Icons/svgs/colors/color-black.svg @@ -1,5 +1,3 @@ - - diff --git a/Resources/Public/Icons/color-blue.svg b/Resources/Public/Icons/svgs/colors/color-blue.svg similarity index 75% rename from Resources/Public/Icons/color-blue.svg rename to Resources/Public/Icons/svgs/colors/color-blue.svg index d9dec40..f6ed50f 100644 --- a/Resources/Public/Icons/color-blue.svg +++ b/Resources/Public/Icons/svgs/colors/color-blue.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/color-gray.svg b/Resources/Public/Icons/svgs/colors/color-gray.svg similarity index 75% rename from Resources/Public/Icons/color-gray.svg rename to Resources/Public/Icons/svgs/colors/color-gray.svg index a0e63f7..ac566e3 100644 --- a/Resources/Public/Icons/color-gray.svg +++ b/Resources/Public/Icons/svgs/colors/color-gray.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/color-green.svg b/Resources/Public/Icons/svgs/colors/color-green.svg similarity index 75% rename from Resources/Public/Icons/color-green.svg rename to Resources/Public/Icons/svgs/colors/color-green.svg index 4c6d396..e3245fd 100644 --- a/Resources/Public/Icons/color-green.svg +++ b/Resources/Public/Icons/svgs/colors/color-green.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/svgs/colors/color-orange.svg b/Resources/Public/Icons/svgs/colors/color-orange.svg new file mode 100644 index 0000000..be6c4e7 --- /dev/null +++ b/Resources/Public/Icons/svgs/colors/color-orange.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/svgs/colors/color-purple.svg b/Resources/Public/Icons/svgs/colors/color-purple.svg new file mode 100644 index 0000000..05f2076 --- /dev/null +++ b/Resources/Public/Icons/svgs/colors/color-purple.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/color-red.svg b/Resources/Public/Icons/svgs/colors/color-red.svg similarity index 75% rename from Resources/Public/Icons/color-red.svg rename to Resources/Public/Icons/svgs/colors/color-red.svg index a9edad8..b16711b 100644 --- a/Resources/Public/Icons/color-red.svg +++ b/Resources/Public/Icons/svgs/colors/color-red.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/color-yellow.svg b/Resources/Public/Icons/svgs/colors/color-yellow.svg similarity index 75% rename from Resources/Public/Icons/color-yellow.svg rename to Resources/Public/Icons/svgs/colors/color-yellow.svg index 45d0b55..c87c9f3 100644 --- a/Resources/Public/Icons/color-yellow.svg +++ b/Resources/Public/Icons/svgs/colors/color-yellow.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/svgs/exclamations/exclamation-black.svg b/Resources/Public/Icons/svgs/exclamations/exclamation-black.svg new file mode 100644 index 0000000..b380866 --- /dev/null +++ b/Resources/Public/Icons/svgs/exclamations/exclamation-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/svgs/exclamations/exclamation-blue.svg b/Resources/Public/Icons/svgs/exclamations/exclamation-blue.svg new file mode 100644 index 0000000..8f7514b --- /dev/null +++ b/Resources/Public/Icons/svgs/exclamations/exclamation-blue.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/exclamations/exclamation-green.svg b/Resources/Public/Icons/svgs/exclamations/exclamation-green.svg new file mode 100644 index 0000000..54e68a7 --- /dev/null +++ b/Resources/Public/Icons/svgs/exclamations/exclamation-green.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/exclamations/exclamation-orange.svg b/Resources/Public/Icons/svgs/exclamations/exclamation-orange.svg new file mode 100644 index 0000000..1f4cc07 --- /dev/null +++ b/Resources/Public/Icons/svgs/exclamations/exclamation-orange.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/exclamations/exclamation-purple.svg b/Resources/Public/Icons/svgs/exclamations/exclamation-purple.svg new file mode 100644 index 0000000..dbc396c --- /dev/null +++ b/Resources/Public/Icons/svgs/exclamations/exclamation-purple.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/exclamations/exclamation-red.svg b/Resources/Public/Icons/svgs/exclamations/exclamation-red.svg new file mode 100644 index 0000000..97ff4b3 --- /dev/null +++ b/Resources/Public/Icons/svgs/exclamations/exclamation-red.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/exclamations/exclamation-yellow.svg b/Resources/Public/Icons/svgs/exclamations/exclamation-yellow.svg new file mode 100644 index 0000000..dca0f42 --- /dev/null +++ b/Resources/Public/Icons/svgs/exclamations/exclamation-yellow.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/flag-black.svg b/Resources/Public/Icons/svgs/flags/flag-black.svg similarity index 85% rename from Resources/Public/Icons/flag-black.svg rename to Resources/Public/Icons/svgs/flags/flag-black.svg index a380b20..42df579 100644 --- a/Resources/Public/Icons/flag-black.svg +++ b/Resources/Public/Icons/svgs/flags/flag-black.svg @@ -1,7 +1,6 @@ - - + - + diff --git a/Resources/Public/Icons/flag-blue.svg b/Resources/Public/Icons/svgs/flags/flag-blue.svg similarity index 81% rename from Resources/Public/Icons/flag-blue.svg rename to Resources/Public/Icons/svgs/flags/flag-blue.svg index 1029429..24d059b 100644 --- a/Resources/Public/Icons/flag-blue.svg +++ b/Resources/Public/Icons/svgs/flags/flag-blue.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/flag-gray.svg b/Resources/Public/Icons/svgs/flags/flag-gray.svg similarity index 81% rename from Resources/Public/Icons/flag-gray.svg rename to Resources/Public/Icons/svgs/flags/flag-gray.svg index 5f983c9..4589301 100644 --- a/Resources/Public/Icons/flag-gray.svg +++ b/Resources/Public/Icons/svgs/flags/flag-gray.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/flag-green.svg b/Resources/Public/Icons/svgs/flags/flag-green.svg similarity index 81% rename from Resources/Public/Icons/flag-green.svg rename to Resources/Public/Icons/svgs/flags/flag-green.svg index 7665d7b..0745873 100644 --- a/Resources/Public/Icons/flag-green.svg +++ b/Resources/Public/Icons/svgs/flags/flag-green.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/svgs/flags/flag-orange.svg b/Resources/Public/Icons/svgs/flags/flag-orange.svg new file mode 100644 index 0000000..2b6e95f --- /dev/null +++ b/Resources/Public/Icons/svgs/flags/flag-orange.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/Public/Icons/svgs/flags/flag-purple.svg b/Resources/Public/Icons/svgs/flags/flag-purple.svg new file mode 100644 index 0000000..c2d5aa8 --- /dev/null +++ b/Resources/Public/Icons/svgs/flags/flag-purple.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/Public/Icons/flag-red.svg b/Resources/Public/Icons/svgs/flags/flag-red.svg similarity index 81% rename from Resources/Public/Icons/flag-red.svg rename to Resources/Public/Icons/svgs/flags/flag-red.svg index 565134e..b009ee8 100644 --- a/Resources/Public/Icons/flag-red.svg +++ b/Resources/Public/Icons/svgs/flags/flag-red.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/flag-toolbar.svg b/Resources/Public/Icons/svgs/flags/flag-toolbar.svg similarity index 78% rename from Resources/Public/Icons/flag-toolbar.svg rename to Resources/Public/Icons/svgs/flags/flag-toolbar.svg index da0449a..41e3383 100644 --- a/Resources/Public/Icons/flag-toolbar.svg +++ b/Resources/Public/Icons/svgs/flags/flag-toolbar.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/flag-white.svg b/Resources/Public/Icons/svgs/flags/flag-white.svg similarity index 80% rename from Resources/Public/Icons/flag-white.svg rename to Resources/Public/Icons/svgs/flags/flag-white.svg index 4294f6b..c901a9d 100644 --- a/Resources/Public/Icons/flag-white.svg +++ b/Resources/Public/Icons/svgs/flags/flag-white.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/flag-yellow.svg b/Resources/Public/Icons/svgs/flags/flag-yellow.svg similarity index 81% rename from Resources/Public/Icons/flag-yellow.svg rename to Resources/Public/Icons/svgs/flags/flag-yellow.svg index e68f0fc..5080f51 100644 --- a/Resources/Public/Icons/flag-yellow.svg +++ b/Resources/Public/Icons/svgs/flags/flag-yellow.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/heart-black.svg b/Resources/Public/Icons/svgs/hearts/heart-black.svg similarity index 100% rename from Resources/Public/Icons/heart-black.svg rename to Resources/Public/Icons/svgs/hearts/heart-black.svg diff --git a/Resources/Public/Icons/heart-blue.svg b/Resources/Public/Icons/svgs/hearts/heart-blue.svg similarity index 77% rename from Resources/Public/Icons/heart-blue.svg rename to Resources/Public/Icons/svgs/hearts/heart-blue.svg index 894186d..4a89cbb 100644 --- a/Resources/Public/Icons/heart-blue.svg +++ b/Resources/Public/Icons/svgs/hearts/heart-blue.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/heart-gray.svg b/Resources/Public/Icons/svgs/hearts/heart-gray.svg similarity index 77% rename from Resources/Public/Icons/heart-gray.svg rename to Resources/Public/Icons/svgs/hearts/heart-gray.svg index 7d98b59..a97419b 100644 --- a/Resources/Public/Icons/heart-gray.svg +++ b/Resources/Public/Icons/svgs/hearts/heart-gray.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/heart-green.svg b/Resources/Public/Icons/svgs/hearts/heart-green.svg similarity index 77% rename from Resources/Public/Icons/heart-green.svg rename to Resources/Public/Icons/svgs/hearts/heart-green.svg index 2f71838..d2adf0a 100644 --- a/Resources/Public/Icons/heart-green.svg +++ b/Resources/Public/Icons/svgs/hearts/heart-green.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/svgs/hearts/heart-orange.svg b/Resources/Public/Icons/svgs/hearts/heart-orange.svg new file mode 100644 index 0000000..ae4d1c2 --- /dev/null +++ b/Resources/Public/Icons/svgs/hearts/heart-orange.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/svgs/hearts/heart-purple.svg b/Resources/Public/Icons/svgs/hearts/heart-purple.svg new file mode 100644 index 0000000..c69b476 --- /dev/null +++ b/Resources/Public/Icons/svgs/hearts/heart-purple.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/heart-red.svg b/Resources/Public/Icons/svgs/hearts/heart-red.svg similarity index 77% rename from Resources/Public/Icons/heart-red.svg rename to Resources/Public/Icons/svgs/hearts/heart-red.svg index 5a33bc2..9a26509 100644 --- a/Resources/Public/Icons/heart-red.svg +++ b/Resources/Public/Icons/svgs/hearts/heart-red.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/heart-yellow.svg b/Resources/Public/Icons/svgs/hearts/heart-yellow.svg similarity index 77% rename from Resources/Public/Icons/heart-yellow.svg rename to Resources/Public/Icons/svgs/hearts/heart-yellow.svg index b08f993..357194d 100644 --- a/Resources/Public/Icons/heart-yellow.svg +++ b/Resources/Public/Icons/svgs/hearts/heart-yellow.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/svgs/infos/info-black.svg b/Resources/Public/Icons/svgs/infos/info-black.svg new file mode 100644 index 0000000..62fe877 --- /dev/null +++ b/Resources/Public/Icons/svgs/infos/info-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/svgs/infos/info-blue.svg b/Resources/Public/Icons/svgs/infos/info-blue.svg new file mode 100644 index 0000000..b5fea5a --- /dev/null +++ b/Resources/Public/Icons/svgs/infos/info-blue.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/info-gray.svg b/Resources/Public/Icons/svgs/infos/info-gray.svg similarity index 80% rename from Resources/Public/Icons/info-gray.svg rename to Resources/Public/Icons/svgs/infos/info-gray.svg index 9bfc505..fa87f06 100644 --- a/Resources/Public/Icons/info-gray.svg +++ b/Resources/Public/Icons/svgs/infos/info-gray.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/svgs/infos/info-green.svg b/Resources/Public/Icons/svgs/infos/info-green.svg new file mode 100644 index 0000000..10eae6d --- /dev/null +++ b/Resources/Public/Icons/svgs/infos/info-green.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/infos/info-orange.svg b/Resources/Public/Icons/svgs/infos/info-orange.svg new file mode 100644 index 0000000..fb31fd3 --- /dev/null +++ b/Resources/Public/Icons/svgs/infos/info-orange.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/infos/info-purple.svg b/Resources/Public/Icons/svgs/infos/info-purple.svg new file mode 100644 index 0000000..4a313e5 --- /dev/null +++ b/Resources/Public/Icons/svgs/infos/info-purple.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/infos/info-red.svg b/Resources/Public/Icons/svgs/infos/info-red.svg new file mode 100644 index 0000000..4352fb9 --- /dev/null +++ b/Resources/Public/Icons/svgs/infos/info-red.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/infos/info-yellow.svg b/Resources/Public/Icons/svgs/infos/info-yellow.svg new file mode 100644 index 0000000..1f59dd2 --- /dev/null +++ b/Resources/Public/Icons/svgs/infos/info-yellow.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/rockets/rocket-black.svg b/Resources/Public/Icons/svgs/rockets/rocket-black.svg new file mode 100644 index 0000000..9012ef2 --- /dev/null +++ b/Resources/Public/Icons/svgs/rockets/rocket-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/svgs/rockets/rocket-blue.svg b/Resources/Public/Icons/svgs/rockets/rocket-blue.svg new file mode 100644 index 0000000..e6dabfd --- /dev/null +++ b/Resources/Public/Icons/svgs/rockets/rocket-blue.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/rockets/rocket-green.svg b/Resources/Public/Icons/svgs/rockets/rocket-green.svg new file mode 100644 index 0000000..b0591c6 --- /dev/null +++ b/Resources/Public/Icons/svgs/rockets/rocket-green.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/rockets/rocket-orange.svg b/Resources/Public/Icons/svgs/rockets/rocket-orange.svg new file mode 100644 index 0000000..927ed2a --- /dev/null +++ b/Resources/Public/Icons/svgs/rockets/rocket-orange.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/rockets/rocket-purple.svg b/Resources/Public/Icons/svgs/rockets/rocket-purple.svg new file mode 100644 index 0000000..d6ed972 --- /dev/null +++ b/Resources/Public/Icons/svgs/rockets/rocket-purple.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/rockets/rocket-red.svg b/Resources/Public/Icons/svgs/rockets/rocket-red.svg new file mode 100644 index 0000000..638f1b1 --- /dev/null +++ b/Resources/Public/Icons/svgs/rockets/rocket-red.svg @@ -0,0 +1 @@ + diff --git a/Resources/Public/Icons/svgs/rockets/rocket-yellow.svg b/Resources/Public/Icons/svgs/rockets/rocket-yellow.svg new file mode 100644 index 0000000..9012ef2 --- /dev/null +++ b/Resources/Public/Icons/svgs/rockets/rocket-yellow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Public/Icons/star-black.svg b/Resources/Public/Icons/svgs/stars/star-black.svg similarity index 100% rename from Resources/Public/Icons/star-black.svg rename to Resources/Public/Icons/svgs/stars/star-black.svg diff --git a/Resources/Public/Icons/star-blue.svg b/Resources/Public/Icons/svgs/stars/star-blue.svg similarity index 79% rename from Resources/Public/Icons/star-blue.svg rename to Resources/Public/Icons/svgs/stars/star-blue.svg index e2dad40..1ad6d79 100644 --- a/Resources/Public/Icons/star-blue.svg +++ b/Resources/Public/Icons/svgs/stars/star-blue.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/star-gray.svg b/Resources/Public/Icons/svgs/stars/star-gray.svg similarity index 79% rename from Resources/Public/Icons/star-gray.svg rename to Resources/Public/Icons/svgs/stars/star-gray.svg index 2c01112..f516f4b 100644 --- a/Resources/Public/Icons/star-gray.svg +++ b/Resources/Public/Icons/svgs/stars/star-gray.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/star-green.svg b/Resources/Public/Icons/svgs/stars/star-green.svg similarity index 79% rename from Resources/Public/Icons/star-green.svg rename to Resources/Public/Icons/svgs/stars/star-green.svg index df13043..0693963 100644 --- a/Resources/Public/Icons/star-green.svg +++ b/Resources/Public/Icons/svgs/stars/star-green.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/svgs/stars/star-orange.svg b/Resources/Public/Icons/svgs/stars/star-orange.svg new file mode 100644 index 0000000..32a5dc0 --- /dev/null +++ b/Resources/Public/Icons/svgs/stars/star-orange.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/Public/Icons/svgs/stars/star-purple.svg b/Resources/Public/Icons/svgs/stars/star-purple.svg new file mode 100644 index 0000000..9d64597 --- /dev/null +++ b/Resources/Public/Icons/svgs/stars/star-purple.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/Public/Icons/star-red.svg b/Resources/Public/Icons/svgs/stars/star-red.svg similarity index 79% rename from Resources/Public/Icons/star-red.svg rename to Resources/Public/Icons/svgs/stars/star-red.svg index 22823c5..d7d5149 100644 --- a/Resources/Public/Icons/star-red.svg +++ b/Resources/Public/Icons/svgs/stars/star-red.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/star-yellow.svg b/Resources/Public/Icons/svgs/stars/star-yellow.svg similarity index 79% rename from Resources/Public/Icons/star-yellow.svg rename to Resources/Public/Icons/svgs/stars/star-yellow.svg index b5b182b..d6b6208 100644 --- a/Resources/Public/Icons/star-yellow.svg +++ b/Resources/Public/Icons/svgs/stars/star-yellow.svg @@ -1,5 +1,4 @@ - - + diff --git a/Resources/Public/Icons/svgs/tags/tag-black.svg b/Resources/Public/Icons/svgs/tags/tag-black.svg new file mode 100644 index 0000000..8aec65b --- /dev/null +++ b/Resources/Public/Icons/svgs/tags/tag-black.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Resources/Public/Icons/tag-blue.svg b/Resources/Public/Icons/svgs/tags/tag-blue.svg similarity index 83% rename from Resources/Public/Icons/tag-blue.svg rename to Resources/Public/Icons/svgs/tags/tag-blue.svg index 6e8e020..5c07d18 100644 --- a/Resources/Public/Icons/tag-blue.svg +++ b/Resources/Public/Icons/svgs/tags/tag-blue.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/tag-gray.svg b/Resources/Public/Icons/svgs/tags/tag-gray.svg similarity index 83% rename from Resources/Public/Icons/tag-gray.svg rename to Resources/Public/Icons/svgs/tags/tag-gray.svg index fa4bf8e..6fbdcf9 100644 --- a/Resources/Public/Icons/tag-gray.svg +++ b/Resources/Public/Icons/svgs/tags/tag-gray.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/tag-green.svg b/Resources/Public/Icons/svgs/tags/tag-green.svg similarity index 83% rename from Resources/Public/Icons/tag-green.svg rename to Resources/Public/Icons/svgs/tags/tag-green.svg index af0bb2d..bc1ab18 100644 --- a/Resources/Public/Icons/tag-green.svg +++ b/Resources/Public/Icons/svgs/tags/tag-green.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/svgs/tags/tag-orange.svg b/Resources/Public/Icons/svgs/tags/tag-orange.svg new file mode 100644 index 0000000..5be328b --- /dev/null +++ b/Resources/Public/Icons/svgs/tags/tag-orange.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/Public/Icons/svgs/tags/tag-purple.svg b/Resources/Public/Icons/svgs/tags/tag-purple.svg new file mode 100644 index 0000000..fce9e89 --- /dev/null +++ b/Resources/Public/Icons/svgs/tags/tag-purple.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/Public/Icons/tag-red.svg b/Resources/Public/Icons/svgs/tags/tag-red.svg similarity index 83% rename from Resources/Public/Icons/tag-red.svg rename to Resources/Public/Icons/svgs/tags/tag-red.svg index 982d41f..3f34fd6 100644 --- a/Resources/Public/Icons/tag-red.svg +++ b/Resources/Public/Icons/svgs/tags/tag-red.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/tag-yellow.svg b/Resources/Public/Icons/svgs/tags/tag-yellow.svg similarity index 83% rename from Resources/Public/Icons/tag-yellow.svg rename to Resources/Public/Icons/svgs/tags/tag-yellow.svg index cb14146..85a2861 100644 --- a/Resources/Public/Icons/tag-yellow.svg +++ b/Resources/Public/Icons/svgs/tags/tag-yellow.svg @@ -1,8 +1,7 @@ - - + - + diff --git a/Resources/Public/Icons/svgs/thumbtacks/thumbtack-black.svg b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-black.svg new file mode 100644 index 0000000..421c01e --- /dev/null +++ b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-black.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/svgs/thumbtacks/thumbtack-blue.svg b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-blue.svg new file mode 100644 index 0000000..df67fe9 --- /dev/null +++ b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-blue.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/svgs/thumbtacks/thumbtack-green.svg b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-green.svg new file mode 100644 index 0000000..6bdebe9 --- /dev/null +++ b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-green.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/svgs/thumbtacks/thumbtack-orange.svg b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-orange.svg new file mode 100644 index 0000000..523740b --- /dev/null +++ b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-orange.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/svgs/thumbtacks/thumbtack-purple.svg b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-purple.svg new file mode 100644 index 0000000..37e98d8 --- /dev/null +++ b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-purple.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/svgs/thumbtacks/thumbtack-red.svg b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-red.svg new file mode 100644 index 0000000..afe8199 --- /dev/null +++ b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-red.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/svgs/thumbtacks/thumbtack-yellow.svg b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-yellow.svg new file mode 100644 index 0000000..6e3378e --- /dev/null +++ b/Resources/Public/Icons/svgs/thumbtacks/thumbtack-yellow.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Resources/Public/Icons/tag-black.svg b/Resources/Public/Icons/tag-black.svg deleted file mode 100644 index e16d7c9..0000000 --- a/Resources/Public/Icons/tag-black.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Resources/Public/JavaScript/comments-modal.js b/Resources/Public/JavaScript/comments-modal.js index 6d0b192..3c0d559 100644 --- a/Resources/Public/JavaScript/comments-modal.js +++ b/Resources/Public/JavaScript/comments-modal.js @@ -3,23 +3,49 @@ */ import AjaxRequest from "@typo3/core/ajax/ajax-request.js"; import Modal from "@typo3/backend/modal.js"; +import NewCommentModal from "@xima/ximatypo3contentplanner/new-comment-modal.js"; class CommentsModal { constructor() { - document.querySelectorAll('.contentPlanner--comments').forEach(item => { + document.querySelectorAll('[data-content-planner-comments]').forEach(item => { item.addEventListener('click', e => { e.preventDefault(); - const url = item.hasAttribute('href') ? item.getAttribute('href') : TYPO3.settings.ajaxUrls.ximatypo3contentplanner_comments; + const url = item.hasAttribute('href') && !item.hasAttribute('data-force-ajax-url') ? item.getAttribute('href') : TYPO3.settings.ajaxUrls.ximatypo3contentplanner_comments; const table = item.getAttribute('data-table'); const id = item.getAttribute('data-id'); - this.fetchComments(url, table, id); + const newCommentsUrl = item.getAttribute('data-new-comment-uri'); + this.fetchComments(url, table, id, newCommentsUrl); }); }); } - fetchComments(href, table, uid) { - new AjaxRequest(href) + fetchComments(url, table, uid, newCommentUrl = false) { + let buttons = [{ + text: TYPO3.lang !== undefined && TYPO3.lang['button.modal.footer.close'] ? TYPO3.lang['button.modal.footer.close'] : 'Close', + name: 'close', + icon: 'actions-close', + active: true, + btnClass: 'btn-secondary', + trigger: function (event, modal) { + modal.hideModal(); + } + }]; + if (newCommentUrl) { + buttons.unshift({ + text: TYPO3.lang !== undefined && TYPO3.lang['button.modal.footer.new'] ? TYPO3.lang['button.modal.footer.new'] : 'New', + name: 'new', + icon: 'actions-message-add', + active: true, + btnClass: 'btn-primary', + trigger: function (event, modal) { + modal.hideModal(); + NewCommentModal.openNewCommentModal(newCommentUrl); + } + }); + } + + new AjaxRequest(url) .withQueryArguments( { table: table, @@ -30,27 +56,15 @@ class CommentsModal { .then(async (response) => { const resolved = await response.resolve(); Modal.advanced({ - title: 'Comments', + title: TYPO3.lang !== undefined && TYPO3.lang['button.modal.header.comments'] ? TYPO3.lang['button.modal.header.comments'] : 'Comments', content: document.createRange() .createContextualFragment(resolved.result), size: Modal.sizes.large, staticBackdrop: true, - buttons: [ - { - text: TYPO3.lang !== undefined ? TYPO3.lang['button.modal.footer.close'] : 'Close', - name: 'close', - icon: 'actions-close', - active: true, - btnClass: 'btn-secondary', - trigger: function (event, modal) { - modal.hideModal(); - } - } - ] + buttons: buttons }); - }); + } } -} -export default new CommentsModal(); + export default new CommentsModal(); diff --git a/Resources/Public/JavaScript/context-menu-actions.js b/Resources/Public/JavaScript/context-menu-actions.js index 53cbcfc..2218133 100644 --- a/Resources/Public/JavaScript/context-menu-actions.js +++ b/Resources/Public/JavaScript/context-menu-actions.js @@ -2,6 +2,8 @@ * Module: @xima/ximatypo3contentplanner/context-menu-actions */ import AjaxRequest from "@typo3/core/ajax/ajax-request.js"; +import Viewport from "@typo3/backend/viewport.js"; +import CommentsModal from "@xima/ximatypo3contentplanner/comments-modal.js"; class ContextMenuActions { @@ -13,6 +15,14 @@ class ContextMenuActions { ContextMenuActions.changeStatus(table, uid, ""); } + load(table, uid, n) { + Viewport.ContentContainer.setUrl(n.uri); + } + + comments(table, uid, n) { + CommentsModal.fetchComments(TYPO3.settings.ajaxUrls.ximatypo3contentplanner_comments, table, uid, n.newCommentUri); + } + static changeStatus(table, uid, status) { new AjaxRequest(top.TYPO3.settings.RecordCommit.moduleUrl + "&data[" + table + "][" + uid + "][tx_ximatypo3contentplanner_status]=" + status) .get() diff --git a/Resources/Public/JavaScript/new-comment-modal.js b/Resources/Public/JavaScript/new-comment-modal.js index 9b74d9b..2da6309 100644 --- a/Resources/Public/JavaScript/new-comment-modal.js +++ b/Resources/Public/JavaScript/new-comment-modal.js @@ -14,48 +14,61 @@ class NewCommentModal { item.addEventListener('click', e => { e.preventDefault() const url = e.currentTarget.getAttribute('href') - Modal.advanced({ - type: Modal.types.iframe, - title: 'New comment', - content: url, - size: Modal.sizes.large, - staticBackdrop: true, - buttons: [ - { - text: TYPO3.lang !== undefined ? TYPO3.lang['button.modal.footer.close'] : 'Close', - name: 'close', - icon: 'actions-close', - active: true, - btnClass: 'btn-secondary', - trigger: function (event, modal) { - Viewport.ContentContainer.refresh(); - modal.hideModal(); - } - } - ], - callback: (modal) => { + this.openNewCommentModal(url) + }) + } + } + + openNewCommentModal(url) { + Modal.advanced({ + type: Modal.types.iframe, + title: 'New comment', + content: url, + size: Modal.sizes.large, + staticBackdrop: true, + buttons: [ + { + text: TYPO3.lang !== undefined && TYPO3.lang['button.modal.footer.close'] ? TYPO3.lang['button.modal.footer.close'] : 'Close', + name: 'close', + icon: 'actions-close', + active: true, + btnClass: 'btn-secondary', + trigger: function (event, modal) { + // workaround: prevent "Do you want to close without saving?" confirmation const iframe = modal.querySelector('.modal-body iframe'); - let loadCount = 0; - - function onIframeLoad(event) { - if (loadCount > 0) { - // workaround: it's the second time the iframe is loaded, so we can assume the comment was saved - Viewport.ContentContainer.refresh(); - modal.hideModal(); - Notification.success('New comment', 'Successfully saved comment to record.'); - } - loadCount++; - iframe.removeEventListener('load', onIframeLoad); - setTimeout(function () { - iframe.addEventListener('load', onIframeLoad); - }, 0); - } + const iframeDocument = iframe.contentWindow.document; + iframeDocument.querySelectorAll('.has-error,.has-change,.is-new').forEach(element => { + element.classList.remove('has-error'); + element.classList.remove('has-change'); + element.classList.remove('is-new'); + }); + Viewport.ContentContainer.refresh(); + modal.hideModal(); + } + } + ], + callback: (modal) => { + const iframe = modal.querySelector('.modal-body iframe'); + let loadCount = 0; + + function onIframeLoad(event) { + if (loadCount > 0) { + // workaround: it's the second time the iframe is loaded, so we can assume the comment was saved + Viewport.ContentContainer.refresh(); + modal.hideModal(); + Notification.success('New comment', 'Successfully saved comment to record.'); + } + loadCount++; + iframe.removeEventListener('load', onIframeLoad); + setTimeout(function () { iframe.addEventListener('load', onIframeLoad); - }, - }); - }) - } + }, 0); + } + + iframe.addEventListener('load', onIframeLoad); + }, + }); } } diff --git a/composer.json b/composer.json index b8a1103..537fd71 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,7 @@ "typo3/class-alias-loader": true, "typo3/cms-composer-installers": true }, + "lock": false, "sort-packages": true }, "extra": { @@ -82,17 +83,17 @@ ], "php:fixer": "php vendor/bin/php-cs-fixer --config=php-cs-fixer.php fix", "php:fixer:check": "php vendor/bin/php-cs-fixer --config=php-cs-fixer.php fix --dry-run --format=checkstyle > php-cs-fixer.xml || true", - "php:lint": "find *.php . -name '*.php' ! -path './vendor/*' ! -path './var/*' ! -path '*node_modules/*' -print0 | xargs -0 -n 1 -P 4 php -l", - "php:lint:check": "find *.php . -name '*.php' ! -path './vendor/*' ! -path './var/*' ! -path '*node_modules/*' -print0 | xargs -0 -n 1 -P 4 php -l", + "php:lint": "find *.php . -name '*.php' ! -path './vendor/*' ! -path './var/*' ! -path './.test/*' ! -path './xima_typo3_content_planner/*' ! -path '*node_modules/*' -print0 | xargs -0 -n 1 -P 4 php -l", + "php:lint:check": "find *.php . -name '*.php' ! -path './vendor/*' ! -path './var/*' ! -path './.test/*' ! -path './xima_typo3_content_planner/*' ! -path '*node_modules/*' -print0 | xargs -0 -n 1 -P 4 php -l", "php:rector": "rector", "php:rector:check": "rector --dry-run", "php:stan": "php vendor/bin/phpstan --generate-baseline=phpstan-baseline.neon --allow-empty-baseline --memory-limit=2G", "php:stan:check": "phpstan --no-progress --error-format=checkstyle > phpstan.xml || true", "typoscript:lint": "typoscript-lint", "typoscript:lint:check": "typoscript-lint --fail-on-warnings", - "xml:lint": "find . -name '*.xlf' ! -path './vendor/*' ! -path './var/*' | xargs -r xmllint --schema vendor/symfony/translation/Resources/schemas/xliff-core-1.2-transitional.xsd --noout", - "xml:lint:check": "find ./ -name '*.xlf' ! -path './vendor/*' ! -path './var/*' | xargs -r xmllint --schema vendor/symfony/translation/Resources/schemas/xliff-core-1.2-transitional.xsd --noout", - "yaml:lint": "find ./ ! -path './vendor/*' ! -path '*/node_modules/*' \\( -name '*.yaml' -o -name '*.yml' \\) | xargs -r yaml-lint", - "yaml:lint:check": "find ./ ! -path './vendor/*' ! -path '*/node_modules/*' \\( -name '*.yaml' -o -name '*.yml' \\) | xargs -r yaml-lint" + "xml:lint": "find . -name '*.xlf' ! -path './vendor/*' ! -path './var/*' ! -path './.test/*' ! -path './xima_typo3_content_planner/*' | xargs -r xmllint --schema vendor/symfony/translation/Resources/schemas/xliff-core-1.2-transitional.xsd --noout", + "xml:lint:check": "find ./ -name '*.xlf' ! -path './vendor/*' ! -path './var/*' ! -path './.test/*' ! -path './xima_typo3_content_planner/*' | xargs -r xmllint --schema vendor/symfony/translation/Resources/schemas/xliff-core-1.2-transitional.xsd --noout", + "yaml:lint": "find ./ ! -path './vendor/*' ! -path '*/node_modules/*' ! -path './.test/*' ! -path './xima_typo3_content_planner/*' \\( -name '*.yaml' -o -name '*.yml' \\) | xargs -r yaml-lint", + "yaml:lint:check": "find ./ ! -path './vendor/*' ! -path '*/node_modules/*' ! -path './.test/*' ! -path './xima_typo3_content_planner/*' \\( -name '*.yaml' -o -name '*.yml' \\) | xargs -r yaml-lint" } } diff --git a/ext_conf_template.txt b/ext_conf_template.txt new file mode 100644 index 0000000..eb02d6d --- /dev/null +++ b/ext_conf_template.txt @@ -0,0 +1,12 @@ +# cat=basic/enable; type=boolean; label=Enable the auto assignment of a record with a new status with the current user +autoAssignment = 1 +# cat=basic/enable; type=boolean; label=Enable the extended menu to show information about assignment and comments +extendedContextMenu = 1 +# cat=basic/enable; type=boolean; label=Enable the current assignee hint to highlight records assigned to the current user +currentAssigneeHighlight = 1 +# cat=basic/enable; type=boolean; label=Delete corresponding comments when status is reset +clearCommentsOnStatusReset = 1 +# cat=basic/enable; type=boolean; label=Enable record edit header info +recordEditHeaderInfo = 1 +# cat=basic/enable; type=boolean; label=Reset status of content element if status on corresponding page is reset +resetContentElementStatusOnPageReset = 0 diff --git a/ext_emconf.php b/ext_emconf.php index 285bd19..f8dc444 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -9,7 +9,7 @@ 'author_email' => 'konrad.michalik@xima.de', 'author_company' => 'XIMA MEDIA GmbH', 'state' => 'stable', - 'version' => '1.0.2', + 'version' => '1.1.0', 'constraints' => [ 'depends' => [ 'php' => '8.1.0-8.3.99', diff --git a/ext_localconf.php b/ext_localconf.php index a028377..bd855bf 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -10,19 +10,10 @@ 'className' => \Xima\XimaTypo3ContentPlanner\Controller\TreeController::class, ]; -$GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'][1719820170] = \Xima\XimaTypo3ContentPlanner\Backend\ToolbarItems\UpdateItem::class; - -$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['ximatypo3contentplanner_toolbarcache'] ??= []; - -$GLOBALS['TYPO3_CONF_VARS']['MAIL']['templateRootPaths'][486] = 'EXT:xima_typo3_content_planner/Resources/Private/Templates/Email/'; +$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['ximatypo3contentplanner_statuscache'] ??= []; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']['ximatypo3contentplanner_statuscache'] = \Xima\XimaTypo3ContentPlanner\Hooks\DataHandlerHook::class . '->clearCachePostProc'; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = \Xima\XimaTypo3ContentPlanner\Hooks\DataHandlerHook::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'][] = \Xima\XimaTypo3ContentPlanner\Hooks\DataHandlerHook::class; $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['registerAdditionalRecordTables'] = []; - -// Feature toggles -$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['features']['autoAssignment'] = true; -$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['features']['autoUnassignment'] = true; - -$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][Configuration::EXT_KEY]['features']['relevantUpdates'] = false; diff --git a/ext_tables.php b/ext_tables.php index 2191db4..dafa64d 100644 --- a/ext_tables.php +++ b/ext_tables.php @@ -13,22 +13,9 @@ 'table' => 'be_users', ]; -$GLOBALS['TYPO3_USER_SETTINGS']['columns']['tx_ximatypo3contentplanner_subscribe'] = [ - 'label' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:be_users.tx_ximatypo3contentplanner_subscribe', - 'description' => 'LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:be_users.tx_ximatypo3contentplanner_subscribe.description', - 'type' => 'select', - 'table' => 'be_users', - 'items' => [ - '' => 'Never', - 'daily' => 'Daily', - 'weekly' => 'Weekly', - ], -]; - $GLOBALS['TYPO3_USER_SETTINGS']['showitem'] .= ', --div--;LLL:EXT:' . Configuration::EXT_KEY . '/Resources/Private/Language/locallang_db.xlf:content_planner, tx_ximatypo3contentplanner_hide, - tx_ximatypo3contentplanner_subscribe, '; $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions']['tx_ximatypo3contentplanner'] = [ diff --git a/ext_tables.sql b/ext_tables.sql index 7fde755..6cca0f3 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -8,8 +8,6 @@ CREATE TABLE pages CREATE TABLE be_users ( tx_ximatypo3contentplanner_hide tinyint(4) unsigned DEFAULT 0 NOT NULL, - tx_ximatypo3contentplanner_subscribe varchar(255) default '' not null, - tx_ximatypo3contentplanner_last_mail int(11) DEFAULT '0' NOT NULL, ); CREATE TABLE tx_ximatypo3contentplanner_comment @@ -23,28 +21,12 @@ CREATE TABLE tx_ximatypo3contentplanner_comment foreign_uid int(11) default '0' not null, foreign_table varchar(255) default '' not null, - sorting int(11) unsigned default '0' not null, content text, author int(11) DEFAULT NULL, PRIMARY KEY (uid) ); -CREATE TABLE tx_ximatypo3contentplanner_note -( - uid int(11) NOT NULL auto_increment, - pid int(11) DEFAULT '0' NOT NULL, - tstamp int(11) DEFAULT '0' NOT NULL, - crdate int(11) DEFAULT '0' NOT NULL, - cruser_id int(11) DEFAULT '0' NOT NULL, - deleted tinyint(4) unsigned DEFAULT '0' NOT NULL, - hidden tinyint(4) unsigned DEFAULT '0' NOT NULL, - - title varchar(255) DEFAULT '' NOT NULL, - content mediumtext, - PRIMARY KEY (uid) -); - CREATE TABLE tx_ximatypo3contentplanner_domain_model_status ( uid int(11) NOT NULL auto_increment, diff --git a/ext_tables_static+adt.sql b/ext_tables_static+adt.sql index f5b1b51..a26ea04 100644 --- a/ext_tables_static+adt.sql +++ b/ext_tables_static+adt.sql @@ -2,5 +2,3 @@ INSERT INTO tx_ximatypo3contentplanner_domain_model_status (uid, pid, tstamp, cr INSERT INTO tx_ximatypo3contentplanner_domain_model_status (uid, pid, tstamp, crdate, deleted, hidden, sorting, cruser_id, title, icon, color) VALUES (2, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 0, 2, 0, 'In progress', 'flag', 'blue'); INSERT INTO tx_ximatypo3contentplanner_domain_model_status (uid, pid, tstamp, crdate, deleted, hidden, sorting, cruser_id, title, icon, color) VALUES (3, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 0, 3, 0, 'Needs review', 'flag', 'yellow'); INSERT INTO tx_ximatypo3contentplanner_domain_model_status (uid, pid, tstamp, crdate, deleted, hidden, sorting, cruser_id, title, icon, color) VALUES (4, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 0, 4, 0, 'Completed', 'flag', 'green'); - -INSERT INTO tx_ximatypo3contentplanner_note (uid, pid, tstamp, crdate, deleted, hidden, title, content) VALUES (1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 0, 'Welcome to Content Planner', '

    This is a quick start note for the TYPO3 extension ‘Content Planner’. Use the dashboard to get a quick overview of the status of the content with the feature-rich widgets and to quickly capture specially assigned content. Further information on use and configuration can be found at https://github.com/xima-media/xima-typo3-content-planner.

    '); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ef730c9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5069 @@ +{ + "name": "html", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "npm-run-all": "^4.1.5", + "svg-sprite": "^2.0.4" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@resvg/resvg-js": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", + "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==", + "dev": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@resvg/resvg-js-android-arm-eabi": "2.6.2", + "@resvg/resvg-js-android-arm64": "2.6.2", + "@resvg/resvg-js-darwin-arm64": "2.6.2", + "@resvg/resvg-js-darwin-x64": "2.6.2", + "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", + "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", + "@resvg/resvg-js-linux-arm64-musl": "2.6.2", + "@resvg/resvg-js-linux-x64-gnu": "2.6.2", + "@resvg/resvg-js-linux-x64-musl": "2.6.2", + "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", + "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", + "@resvg/resvg-js-win32-x64-msvc": "2.6.2" + } + }, + "node_modules/@resvg/resvg-js-android-arm-eabi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz", + "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-android-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz", + "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.2.tgz", + "integrity": "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-x64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz", + "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz", + "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz", + "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz", + "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz", + "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz", + "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-arm64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz", + "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-ia32-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz", + "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-x64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz", + "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-selector-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz", + "integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g==", + "dev": true + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/es-abstract": { + "version": "1.23.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.7.tgz", + "integrity": "sha512-OygGC8kIcDhXX+6yAZRGLqwi2CmEXCbLQixeGUgYeR+Qwlppqmo7DIDr8XibtEBZp+fJcoYpoatp5qwLMEdcqQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.6", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-regex-test": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", + "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", + "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prettysize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prettysize/-/prettysize-2.0.0.tgz", + "integrity": "sha512-VVtxR7sOh0VsG8o06Ttq5TrI1aiZKmC+ClSn4eBPaNf4SHr5lzbYW+kYGX3HocBL/MfpVrRfFZ9V3vCbLaiplg==", + "dev": true + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", + "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "dunder-proto": "^1.0.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-sprite": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-sprite/-/svg-sprite-2.0.4.tgz", + "integrity": "sha512-kjDoATgr4k6tdtfQczpkbuFW6RE7tPUPe/rbRd1n2NV92kdwaXEZMIxJqAZfMGOMfU/Kp1u89SUYsfHCbAvVHg==", + "dev": true, + "dependencies": { + "@resvg/resvg-js": "^2.6.0", + "@xmldom/xmldom": "^0.8.10", + "async": "^3.2.5", + "css-selector-parser": "^1.4.1", + "csso": "^4.2.0", + "cssom": "^0.5.0", + "glob": "^7.2.3", + "js-yaml": "^4.1.0", + "lodash.escape": "^4.0.1", + "lodash.merge": "^4.6.2", + "mustache": "^4.2.0", + "prettysize": "^2.0.0", + "svgo": "^2.8.0", + "vinyl": "^2.2.1", + "winston": "^3.11.0", + "xpath": "^0.0.34", + "yargs": "^17.7.2" + }, + "bin": { + "svg-sprite": "bin/svg-sprite.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xpath": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz", + "integrity": "sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + } + }, + "dependencies": { + "@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true + }, + "@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@resvg/resvg-js": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", + "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==", + "dev": true, + "requires": { + "@resvg/resvg-js-android-arm-eabi": "2.6.2", + "@resvg/resvg-js-android-arm64": "2.6.2", + "@resvg/resvg-js-darwin-arm64": "2.6.2", + "@resvg/resvg-js-darwin-x64": "2.6.2", + "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", + "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", + "@resvg/resvg-js-linux-arm64-musl": "2.6.2", + "@resvg/resvg-js-linux-x64-gnu": "2.6.2", + "@resvg/resvg-js-linux-x64-musl": "2.6.2", + "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", + "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", + "@resvg/resvg-js-win32-x64-msvc": "2.6.2" + } + }, + "@resvg/resvg-js-android-arm-eabi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz", + "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-android-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz", + "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-darwin-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.2.tgz", + "integrity": "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-darwin-x64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz", + "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-linux-arm-gnueabihf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz", + "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-linux-arm64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz", + "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-linux-arm64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz", + "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-linux-x64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz", + "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-linux-x64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz", + "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-win32-arm64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz", + "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-win32-ia32-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz", + "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==", + "dev": true, + "optional": true + }, + "@resvg/resvg-js-win32-x64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz", + "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==", + "dev": true, + "optional": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true + }, + "@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + } + }, + "arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + } + }, + "async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "requires": { + "possible-typed-array-names": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + } + }, + "call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "requires": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-selector-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz", + "integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g==", + "dev": true + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.1.2" + } + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + } + }, + "data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + } + }, + "data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + } + } + }, + "es-abstract": { + "version": "1.23.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.7.tgz", + "integrity": "sha512-OygGC8kIcDhXX+6yAZRGLqwi2CmEXCbLQixeGUgYeR+Qwlppqmo7DIDr8XibtEBZp+fJcoYpoatp5qwLMEdcqQ==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.6", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-regex-test": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + } + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, + "es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + } + }, + "es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "requires": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + } + }, + "escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true + }, + "function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", + "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.0.0" + } + }, + "get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "requires": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + } + }, + "is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + } + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "requires": { + "has-bigints": "^1.0.2" + } + }, + "is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-core-module": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", + "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", + "dev": true, + "requires": { + "hasown": "^2.0.2" + } + }, + "is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + } + }, + "is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, + "is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true + }, + "is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + } + }, + "is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.16" + } + }, + "is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true + }, + "is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "dev": true, + "requires": { + "call-bound": "^1.0.2" + } + }, + "is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "requires": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "requires": { + "fn.name": "1.x.x" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true + }, + "prettysize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prettysize/-/prettysize-2.0.0.tgz", + "integrity": "sha512-VVtxR7sOh0VsG8o06Ttq5TrI1aiZKmC+ClSn4eBPaNf4SHr5lzbYW+kYGX3HocBL/MfpVrRfFZ9V3vCbLaiplg==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "reflect.getprototypeof": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", + "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "dunder-proto": "^1.0.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.1" + } + }, + "regexp.prototype.flags": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "requires": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + } + }, + "safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true + }, + "shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true + }, + "side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + } + }, + "string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "svg-sprite": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-sprite/-/svg-sprite-2.0.4.tgz", + "integrity": "sha512-kjDoATgr4k6tdtfQczpkbuFW6RE7tPUPe/rbRd1n2NV92kdwaXEZMIxJqAZfMGOMfU/Kp1u89SUYsfHCbAvVHg==", + "dev": true, + "requires": { + "@resvg/resvg-js": "^2.6.0", + "@xmldom/xmldom": "^0.8.10", + "async": "^3.2.5", + "css-selector-parser": "^1.4.1", + "csso": "^4.2.0", + "cssom": "^0.5.0", + "glob": "^7.2.3", + "js-yaml": "^4.1.0", + "lodash.escape": "^4.0.1", + "lodash.merge": "^4.6.2", + "mustache": "^4.2.0", + "prettysize": "^2.0.0", + "svgo": "^2.8.0", + "vinyl": "^2.2.1", + "winston": "^3.11.0", + "xpath": "^0.0.34", + "yargs": "^17.7.2" + } + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true + }, + "typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + } + }, + "typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + } + }, + "unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "requires": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + } + }, + "which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "requires": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + } + }, + "which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + } + }, + "winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "requires": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "requires": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "xpath": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz", + "integrity": "sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..46dcd2b --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "devDependencies": { + "npm-run-all": "^4.1.5", + "svg-sprite": "^2.0.4" + }, + "scripts": { + "sprites": "npm-run-all --parallel sprite-*", + "sprite-color": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite colors.svg Resources/Public/Icons/svgs/colors/*.svg", + "sprite-flag": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite flags.svg Resources/Public/Icons/svgs/flags/*.svg", + "sprite-heart": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite hearts.svg Resources/Public/Icons/svgs/hearts/*.svg", + "sprite-info": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite infos.svg Resources/Public/Icons/svgs/infos/*.svg", + "sprite-stars": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite stars.svg Resources/Public/Icons/svgs/stars/*.svg", + "sprite-tags": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite tags.svg Resources/Public/Icons/svgs/tags/*.svg", + "sprite-certificates": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite certificates.svg Resources/Public/Icons/svgs/certificates/*.svg", + "sprite-exclamations": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite exclamations.svg Resources/Public/Icons/svgs/exclamations/*.svg", + "sprite-rockets": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite rockets.svg Resources/Public/Icons/svgs/rockets/*.svg", + "sprite-thumbtacks": "svg-sprite -s --symbol-dest Resources/Public/Icons/sprites/ --symbol-sprite thumbtacks.svg Resources/Public/Icons/svgs/thumbtacks/*.svg" + } +} diff --git a/rector.php b/rector.php index 7fea416..4adc4c3 100644 --- a/rector.php +++ b/rector.php @@ -16,7 +16,6 @@ ->withPaths([ __DIR__ . '/Classes', __DIR__ . '/Configuration', - __DIR__ . '/config', __DIR__ . '/ext_emconf.php', __DIR__ . '/ext_tables.php', ])