diff --git a/.gitattributes b/.gitattributes index c38382e..779ec05 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,7 @@ +/.github/ export-ignore /Build/ export-ignore -/Resources/Private/Php/ export-ignore +/Tests/ export-ignore +/.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore -/.editorconfig export-ignore +/.crowdin.yml export-ignore \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5891ce5..4b821c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,54 +1,22 @@ -# Adapted from https://github.com/TYPO3GmbH/blog/blob/master/.github/workflows/ci.yml -name: CI +name: Tests -on: [pull_request] +on: pull_request jobs: - build: - runs-on: ubuntu-latest + CGL: + name: Coding Style Check (TYPO3 Community CGL) - strategy: - fail-fast: false - matrix: - typo3: [^10.4, ^11.5] - php: ['7.4'] - include: - - typo3: ^11.5 - php: '8.0' - - typo3: ^11.5 - php: '8.1' + runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 - - - name: Setup PHP ${{ matrix.php }}, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: mbstring, dom, zip - - - name: Validate composer.json and composer.lock - run: composer validate + uses: actions/checkout@v4 - - name: Get composer cache directory - id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" - - - name: Cache composer dependencies - uses: actions/cache@v2 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ runner.os }}-composer- - - - name: Install dependencies with typo3/cms-core:${{ matrix.typo3 }} - run: | - composer require typo3/cms-core:${{ matrix.typo3 }} --no-progress - git checkout composer.json - - - name: php-cs-fixer - run: composer ci:php:fixer + - name: Composer + run: Build/Scripts/runTests.sh -p 8.1 -s composerUpdate - name: Lint PHP - run: composer ci:php:lint + run: Build/Scripts/runTests.sh -p 8.1 -s lint + + - name: Validate code against CGL + run: Build/Scripts/runTests.sh -p 8.1 -s cgl -n diff --git a/.gitignore b/.gitignore index 42796e9..63300ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,48 @@ -# Git global ignore file -# for local exclude patterns please edit .git/info/exclude -# Example file see https://github.com/TYPO3-Documentation/T3DocTeam/blob/master/.gitignore +######################## +# plesk-widget +# global ignore file +######################## -# ignore generated documentation +# Ignore files generated by docs rendering *GENERATED* +docker-compose.yaml +docker-compose.yml -# ignore typical clutter of IDEs and editors (this could be added in .git/info/exclude, -# but we add it here for convenience) +# Ignore environment files +.env + +# Ignore temporary files (left by editors and OS) *~ *.bak +*.swp +.DS_Store + +# Ignore by common IDEs used directories/files +nbproject *.idea *.project -*.swp -.project +.buildpath +.settings +.TemporaryItems .webprj +.fleet + +# Temporary files and folders +/.cache +.php_cs.cache +.php-cs-fixer.cache +.sass-cache +.session +*.log + +# Ignore composer stuff +bin/* +vendor/* +.build +.php_cs.cache +composer.lock +# Ignore testing stuff +/.Build +/composer.json.orig +/composer.json.testing diff --git a/Build/FunctionalTests.xml b/Build/FunctionalTests.xml new file mode 100644 index 0000000..59a41b6 --- /dev/null +++ b/Build/FunctionalTests.xml @@ -0,0 +1,28 @@ + + + + ../Tests/Functional/ + + + + + + + diff --git a/Build/FunctionalTestsBootstrap.php b/Build/FunctionalTestsBootstrap.php new file mode 100644 index 0000000..8bad2c5 --- /dev/null +++ b/Build/FunctionalTestsBootstrap.php @@ -0,0 +1,17 @@ +defineOriginalRootPath(); + $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/tests'); + $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/transient'); +}); diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh new file mode 100755 index 0000000..e8f2f11 --- /dev/null +++ b/Build/Scripts/runTests.sh @@ -0,0 +1,606 @@ +#!/usr/bin/env bash + +# +# EXT:examples test runner based on docker/podman. +# + +trap 'cleanUp;exit 2' SIGINT + +waitFor() { + local HOST=${1} + local PORT=${2} + local TESTCOMMAND=" + COUNT=0; + while ! nc -z ${HOST} ${PORT}; do + if [ \"\${COUNT}\" -gt 10 ]; then + echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\"; + exit 1; + fi; + sleep 1; + COUNT=\$((COUNT + 1)); + done; + " + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}" + if [[ $? -gt 0 ]]; then + kill -SIGINT -$$ + fi +} + +cleanUp() { + ATTACHED_CONTAINERS=$(${CONTAINER_BIN} ps --filter network=${NETWORK} --format='{{.Names}}') + for ATTACHED_CONTAINER in ${ATTACHED_CONTAINERS}; do + ${CONTAINER_BIN} rm -f ${ATTACHED_CONTAINER} >/dev/null + done + ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null +} + +cleanCacheFiles() { + echo -n "Clean caches ... " + rm -rf \ + .Build/.cache \ + .php-cs-fixer.cache + echo "done" +} + +cleanRenderedDocumentationFiles() { + echo -n "Clean rendered documentation files ... " + rm -rf \ + Documentation-GENERATED-temp + echo "done" +} + +handleDbmsOptions() { + # -a, -d, -i depend on each other. Validate input combinations and set defaults. + case ${DBMS} in + mariadb) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.4" + if ! [[ ${DBMS_VERSION} =~ ^(10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + mysql) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0" + if ! [[ ${DBMS_VERSION} =~ ^(8.0|8.1|8.2|8.3)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + postgres) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10" + if ! [[ ${DBMS_VERSION} =~ ^(10|11|12|13|14|15|16)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + sqlite) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + if [ -n "${DBMS_VERSION}" ]; then + echo "Invalid combination -d ${DBMS} -i ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + *) + echo "Invalid option -d ${DBMS}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + ;; + esac +} + +loadHelp() { + # Load help text into $HELP + read -r -d '' HELP < + Specifies which test suite to run + - cgl: cgl test and fix all php files + - clean: Clean temporary files + - cleanCache: Clean cache folds for files. + - cleanRenderedDocumentation: Clean existing rendered documentation output. + - composer: "composer" with all remaining arguments dispatched. + - composerNormalize: "composer normalize" + - composerUpdate: "composer update", handy if host has no PHP + - composerUpdateRector: "composer update", for rector subdirectory + - composerValidate: "composer validate" + - functional: PHP functional tests + - lint: PHP linting + - phpstan: PHPStan static analysis + - phpstanBaseline: Generate PHPStan baseline + - unit: PHP unit tests + - rector: Apply Rector rules + - renderDocumentation + - testRenderDocumentation + + -b + Container environment: + - docker + - podman + + If not specified, podman will be used if available. Otherwise, docker is used. + + -a + Only with -s functional|functionalDeprecated + Specifies to use another driver, following combinations are available: + - mysql + - mysqli (default) + - pdo_mysql + - mariadb + - mysqli (default) + - pdo_mysql + + -d + Only with -s functional|functionalDeprecated|acceptance|acceptanceComposer|acceptanceInstall + Specifies on which DBMS tests are performed + - sqlite: (default): use sqlite + - mariadb: use mariadb + - mysql: use MySQL + - postgres: use postgres + + -i version + Specify a specific database version + With "-d mariadb": + - 10.4 short-term, maintained until 2024-06-18 (default) + - 10.5 short-term, maintained until 2025-06-24 + - 10.6 long-term, maintained until 2026-06 + - 10.7 short-term, no longer maintained + - 10.8 short-term, maintained until 2023-05 + - 10.9 short-term, maintained until 2023-08 + - 10.10 short-term, maintained until 2023-11 + - 10.11 long-term, maintained until 2028-02 + - 11.0 development series + - 11.1 short-term development series + With "-d mysql": + - 8.0 maintained until 2026-04 (default) LTS + - 8.1 unmaintained since 2023-10 + - 8.2 unmaintained since 2024-01 + - 8.3 maintained until 2024-04 + With "-d postgres": + - 10 unmaintained since 2022-11-10 (default) + - 11 unmaintained since 2023-11-09 + - 12 maintained until 2024-11-14 + - 13 maintained until 2025-11-13 + - 14 maintained until 2026-11-12 + - 15 maintained until 2027-11-11 + - 16 maintained until 2028-11-09 + + -p <8.1|8.2|8.3> + Specifies the PHP minor version to be used + - 8.1: use PHP 8.1 + - 8.2: use PHP 8.2 + - 8.3: use PHP 8.3 + + -x + Only with -s functional|unit + Send information to host instance for test or system under test break points. This is especially + useful if a local PhpStorm instance is listening on default xdebug port 9003. A different port + can be selected with -y + + -y + Send xdebug information to a different port than default 9003 if an IDE like PhpStorm + is not listening on default port. + + -n + Only with -s cgl, composerNormalize, rector + Activate dry-run in CGL check and composer normalize that does not actively change files and only prints broken ones. + + -u + Update existing typo3/core-testing-*:latest container images and remove dangling local volumes. + New images are published once in a while and only the latest ones are supported by core testing. + Use this if weird test errors occur. Also removes obsolete image versions of typo3/core-testing-*. + + -h + Show this help. + +Examples: + # Run unit tests using PHP 8.2 + ./Build/Scripts/runTests.sh -p 8.2 -s unit + + # Run functional tests using PHP 8.3 and MariaDB 10.6 using pdo_mysql + ./Build/Scripts/runTests.sh -p 8.3 -s functional -d mariadb -i 10.6 -a pdo_mysql + + # Run functional tests on postgres with xdebug, php 8.3 and execute a restricted set of tests + ./Build/Scripts/runTests.sh -x -p 8.3 -s functional -d postgres -- Tests/Functional/DummyTest.php +EOF +} + +# Test if docker exists, else exit out with error +if ! type "docker" >/dev/null 2>&1 && ! type "podman" >/dev/null 2>&1; then + echo "This script relies on docker or podman. Please install" >&2 + exit 1 +fi + +# Option defaults +# @todo Consider to switch from cgl to help as default +TEST_SUITE="cgl" +DATABASE_DRIVER="" +DBMS="sqlite" +DBMS_VERSION="" +PHP_VERSION="8.1" +PHP_XDEBUG_ON=0 +PHP_XDEBUG_PORT=9003 +CGLCHECK_DRY_RUN=0 +CI_PARAMS="${CI_PARAMS:-}" +DOCS_PARAMS="${DOCS_PARAMS:=--pull always}" +CONTAINER_BIN="" +CONTAINER_HOST="host.docker.internal" + +# Option parsing updates above default vars +# Reset in case getopts has been used previously in the shell +OPTIND=1 +# Array for invalid options +INVALID_OPTIONS=() +# Simple option parsing based on getopts (! not getopt) +while getopts "a:b:d:i:s:p:xy:nhu" OPT; do + case ${OPT} in + a) + DATABASE_DRIVER=${OPTARG} + ;; + s) + TEST_SUITE=${OPTARG} + ;; + b) + if ! [[ ${OPTARG} =~ ^(docker|podman)$ ]]; then + INVALID_OPTIONS+=("${OPTARG}") + fi + CONTAINER_BIN=${OPTARG} + ;; + d) + DBMS=${OPTARG} + ;; + i) + DBMS_VERSION=${OPTARG} + ;; + p) + PHP_VERSION=${OPTARG} + if ! [[ ${PHP_VERSION} =~ ^(8.1|8.2|8.3)$ ]]; then + INVALID_OPTIONS+=("p ${OPTARG}") + fi + ;; + x) + PHP_XDEBUG_ON=1 + ;; + y) + PHP_XDEBUG_PORT=${OPTARG} + ;; + n) + CGLCHECK_DRY_RUN=1 + ;; + h) + loadHelp + echo "${HELP}" + exit 0 + ;; + u) + TEST_SUITE=update + ;; + \?) + INVALID_OPTIONS+=("${OPTARG}") + ;; + :) + INVALID_OPTIONS+=("${OPTARG}") + ;; + esac +done + +# Exit on invalid options +if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then + echo "Invalid option(s):" >&2 + for I in "${INVALID_OPTIONS[@]}"; do + echo "-"${I} >&2 + done + echo >&2 + echo "call \".Build/Scripts/runTests.sh -h\" to display help and valid options" + exit 1 +fi + +handleDbmsOptions + +COMPOSER_ROOT_VERSION="13.0.x-dev" +HOST_UID=$(id -u) +USERSET="" +if [ $(uname) != "Darwin" ]; then + USERSET="--user $HOST_UID" +fi + +# Go to the directory this script is located, so everything else is relative +# to this dir, no matter from where this script is called, then go up two dirs. +THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd "$THIS_SCRIPT_DIR" || exit 1 +cd ../../ || exit 1 +ROOT_DIR="${PWD}" + +# Create .cache dir: composer need this. +mkdir -p .Build/.cache +mkdir -p .Build/web/typo3temp/var/tests + +IMAGE_PREFIX="docker.io/" +# Non-CI fetches TYPO3 images (php and nodejs) from ghcr.io +TYPO3_IMAGE_PREFIX="ghcr.io/typo3/" +CONTAINER_INTERACTIVE="-it --init" + +IS_CORE_CI=0 +# ENV var "CI" is set by gitlab-ci. We use it here to distinct 'local' and 'CI' environment. +if [ "${CI}" == "true" ]; then + IS_CORE_CI=1 + IMAGE_PREFIX="" + CONTAINER_INTERACTIVE="" +fi + +# determine default container binary to use: 1. podman 2. docker +if [[ -z "${CONTAINER_BIN}" ]]; then + if type "podman" >/dev/null 2>&1; then + CONTAINER_BIN="podman" + elif type "docker" >/dev/null 2>&1; then + CONTAINER_BIN="docker" + fi +fi + +IMAGE_PHP="${TYPO3_IMAGE_PREFIX}core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest" +IMAGE_ALPINE="${IMAGE_PREFIX}alpine:3.8" +IMAGE_MARIADB="docker.io/mariadb:${DBMS_VERSION}" +IMAGE_MYSQL="docker.io/mysql:${DBMS_VERSION}" +IMAGE_POSTGRES="docker.io/postgres:${DBMS_VERSION}-alpine" +IMAGE_DOCS="ghcr.io/typo3-documentation/render-guides:latest" + +# Set $1 to first mass argument, this is the optional test file or test directory to execute +shift $((OPTIND - 1)) + +SUFFIX=$(echo $RANDOM) +NETWORK="t3docsexamples-${SUFFIX}" +${CONTAINER_BIN} network create ${NETWORK} >/dev/null + +if [ ${CONTAINER_BIN} = "docker" ]; then + # docker needs the add-host for xdebug remote debugging. podman has host.container.internal built in + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" + CONTAINER_DOCS_PARAMS="${CONTAINER_INTERACTIVE} ${DOCS_PARAMS} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:/project" +else + # podman + CONTAINER_HOST="host.containers.internal" + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" + CONTAINER_DOCS_PARAMS="${CONTAINER_INTERACTIVE} ${DOCS_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:/project" +fi + +if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE="-e XDEBUG_MODE=off" + XDEBUG_CONFIG=" " +else + XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo" + XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=${CONTAINER_HOST}" +fi + +# Suite execution +case ${TEST_SUITE} in + cgl) + if [ "${CGLCHECK_DRY_RUN}" -eq 1 ]; then + COMMAND="php -dxdebug.mode=off .Build/bin/php-cs-fixer fix -v --dry-run --diff --config=Build/cgl/.php-cs-fixer.dist.php --using-cache=no ." + else + COMMAND="php -dxdebug.mode=off .Build/bin/php-cs-fixer fix -v --config=Build/cgl/.php-cs-fixer.dist.php --using-cache=no ." + fi + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name cgl-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + clean) + cleanCacheFiles + cleanRenderedDocumentationFiles + ;; + cleanCache) + cleanCacheFiles + ;; + cleanRenderedDocumentation) + cleanRenderedDocumentationFiles + ;; + composer) + COMMAND=(composer "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + composerNormalize) + if [ "${CGLCHECK_DRY_RUN}" -eq 1 ]; then + COMMAND=(composer normalize -n) + else + COMMAND=(composer normalize) + fi + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + composerUpdate) + rm -rf .Build/bin/ .Build/typo3 .Build/vendor .Build/Web ./composer.lock + cp ${ROOT_DIR}/composer.json ${ROOT_DIR}/composer.json.orig + if [ -f "${ROOT_DIR}/composer.json.testing" ]; then + cp ${ROOT_DIR}/composer.json ${ROOT_DIR}/composer.json.orig + fi + COMMAND=(composer require --no-ansi --no-interaction --no-progress) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + cp ${ROOT_DIR}/composer.json ${ROOT_DIR}/composer.json.testing + mv ${ROOT_DIR}/composer.json.orig ${ROOT_DIR}/composer.json + ;; + composerUpdateRector) + rm -rf Build/rector/.Build/bin/ Build/rector/.Build/vendor Build/rector/composer.lock + cp ${ROOT_DIR}/Build/rector/composer.json ${ROOT_DIR}/Build/rector/composer.json.orig + if [ -f "${ROOT_DIR}/Build/rector/composer.json.testing" ]; then + cp ${ROOT_DIR}/Build/rector/composer.json ${ROOT_DIR}/Build/rector/composer.json.orig + fi + COMMAND=(composer require --working-dir=${ROOT_DIR}/Build/rector --no-ansi --no-interaction --no-progress) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + cp ${ROOT_DIR}/Build/rector/composer.json ${ROOT_DIR}/Build/rector/composer.json.testing + mv ${ROOT_DIR}/Build/rector/composer.json.orig ${ROOT_DIR}/Build/rector/composer.json + ;; + composerValidate) + COMMAND=(composer validate "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + functional) + CONTAINER_PARAMS="" + COMMAND=(.Build/bin/phpunit -c Build/phpunit/FunctionalTests.xml --exclude-group not-${DBMS} ${EXTRA_TEST_OPTIONS} "$@") + case ${DBMS} in + mariadb) + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mariadb-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + mysql) + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mysql-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + postgres) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-func-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-func-${SUFFIX} 5432 + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=bamboo -e typo3DatabaseUsername=funcu -e typo3DatabaseHost=postgres-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + sqlite) + # create sqlite tmpfs mount typo3temp/var/tests/functional-sqlite-dbs/ to avoid permission issues + mkdir -p "${ROOT_DIR}/.Build/web/typo3temp/var/tests/functional-sqlite-dbs/" + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${ROOT_DIR}/.Build/web/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + esac + ;; + lint) + COMMAND="find . -name \\*.php ! -path "./.Build/\\*" -print0 | xargs -0 -n1 -P4 php -dxdebug.mode=off -l >/dev/null" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + phpstan) + COMMAND="php -dxdebug.mode=off .Build/bin/phpstan --configuration=Build/phpstan/phpstan.neon" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + phpstanBaseline) + COMMAND="php -dxdebug.mode=off .Build/bin/phpstan --configuration=Build/phpstan/phpstan.neon --generate-baseline=Build/phpstan/phpstan-baseline.neon -v" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + rector) + if [ "${CGLCHECK_DRY_RUN}" -eq 1 ]; then + COMMAND=(php -dxdebug.mode=off Build/rector/.Build/bin/rector -n --config=Build/rector/rector.php --clear-cache "$@") + else + COMMAND=(php -dxdebug.mode=off Build/rector/.Build/bin/rector --config=Build/rector/rector.php --clear-cache "$@") + fi + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name rector-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + renderDocumentation) + COMMAND=(--config=Documentation "$@") + mkdir -p Documentation-GENERATED-temp + ${CONTAINER_BIN} run ${CONTAINER_INTERACTIVE} ${CONTAINER_DOCS_PARAMS} --name render-documentation-${SUFFIX} ${IMAGE_DOCS} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + testRenderDocumentation) + COMMAND=(--config=Documentation --no-progress --fail-on-log "$@") + mkdir -p Documentation-GENERATED-temp + ${CONTAINER_BIN} run ${CONTAINER_INTERACTIVE} ${CONTAINER_DOCS_PARAMS} --name render-documentation-test-${SUFFIX} ${IMAGE_DOCS} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + unit) + COMMAND=(.Build/bin/phpunit -c Build/phpunit/UnitTests.xml ${EXTRA_TEST_OPTIONS} "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + update) + # pull typo3/core-testing-* versions of those ones that exist locally + echo "> pull ${TYPO3_IMAGE_PREFIX}core-testing-* versions of those ones that exist locally" + ${CONTAINER_BIN} images "${TYPO3_IMAGE_PREFIX}core-testing-*" --format "{{.Repository}}:{{.Tag}}" | xargs -I {} ${CONTAINER_BIN} pull {} + echo "" + # remove "dangling" typo3/core-testing-* images (those tagged as ) + echo "> remove \"dangling\" ${TYPO3_IMAGE_PREFIX}/core-testing-* images (those tagged as )" + ${CONTAINER_BIN} images --filter "reference=${TYPO3_IMAGE_PREFIX}/core-testing-*" --filter "dangling=true" --format "{{.ID}}" | xargs -I {} ${CONTAINER_BIN} rmi -f {} + echo "" + ;; + *) + loadHelp + echo "Invalid -s option argument ${TEST_SUITE}" >&2 + echo >&2 + echo "${HELP}" >&2 + exit 1 + ;; +esac + +cleanUp + +# Print summary +echo "" >&2 +echo "###########################################################################" >&2 +echo "Result of ${TEST_SUITE}" >&2 +echo "Container runtime: ${CONTAINER_BIN}" >&2 +if [[ ${IS_CORE_CI} -eq 1 ]]; then + echo "Environment: CI" >&2 +else + echo "Environment: local" >&2 +fi +echo "PHP: ${PHP_VERSION}" >&2 +echo "TYPO3: ${CORE_VERSION}" >&2 +if [[ ${TEST_SUITE} =~ ^functional$ ]]; then + case "${DBMS}" in + mariadb|mysql) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 + ;; + postgres) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver pdo_pgsql" >&2 + ;; + sqlite) + echo "DBMS: ${DBMS} driver pdo_sqlite" >&2 + ;; + esac +fi +if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + echo "SUCCESS" >&2 +else + echo "FAILURE" >&2 +fi +echo "###########################################################################" >&2 +echo "" >&2 + +# Exit with code of test suite - This script return non-zero if the executed test failed. +exit $SUITE_EXIT_CODE \ No newline at end of file diff --git a/Build/UnitTests.xml b/Build/UnitTests.xml new file mode 100644 index 0000000..a90da4c --- /dev/null +++ b/Build/UnitTests.xml @@ -0,0 +1,29 @@ + + + + ../Tests/Unit/ + + + + + + + diff --git a/Build/UnitTestsBootstrap.php b/Build/UnitTestsBootstrap.php new file mode 100644 index 0000000..8a8c307 --- /dev/null +++ b/Build/UnitTestsBootstrap.php @@ -0,0 +1,75 @@ +getWebRoot(), '/')); + } + if (!getenv('TYPO3_PATH_WEB')) { + putenv('TYPO3_PATH_WEB=' . rtrim($testbase->getWebRoot(), '/')); + } + + $testbase->defineSitePath(); + + // We can use the "typo3/cms-composer-installers" constant "TYPO3_COMPOSER_MODE" to determine composer mode. + // This should be always true except for TYPO3 mono repository. + $composerMode = defined('TYPO3_COMPOSER_MODE') && TYPO3_COMPOSER_MODE === true; + $requestType = \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_BE | \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_CLI; + SystemEnvironmentBuilder::run(0, $requestType, $composerMode); + + $testbase->createDirectory(Environment::getPublicPath() . '/typo3conf/ext'); + $testbase->createDirectory(Environment::getPublicPath() . '/typo3temp/assets'); + $testbase->createDirectory(Environment::getPublicPath() . '/typo3temp/var/tests'); + $testbase->createDirectory(Environment::getPublicPath() . '/typo3temp/var/transient'); + + // Retrieve an instance of class loader and inject to core bootstrap + $classLoader = require $testbase->getPackagesPath() . '/autoload.php'; + Bootstrap::initializeClassLoader($classLoader); + + // Initialize default TYPO3_CONF_VARS + $configurationManager = new ConfigurationManager(); + $GLOBALS['TYPO3_CONF_VARS'] = $configurationManager->getDefaultConfiguration(); + + $cache = new PhpFrontend( + 'core', + new NullBackend('production', []) + ); + $packageManager = Bootstrap::createPackageManager( + UnitTestPackageManager::class, + Bootstrap::createPackageCache($cache) + ); + + GeneralUtility::setSingletonInstance(PackageManager::class, $packageManager); + ExtensionManagementUtility::setPackageManager($packageManager); + + $testbase->dumpClassLoadingInformation(); + + GeneralUtility::purgeInstances(); +})(); diff --git a/Build/.php_cs.php b/Build/cgl/.php-cs-fixer.dist.php similarity index 67% rename from Build/.php_cs.php rename to Build/cgl/.php-cs-fixer.dist.php index cb8f19e..c5d37fd 100644 --- a/Build/.php_cs.php +++ b/Build/cgl/.php-cs-fixer.dist.php @@ -1,21 +1,17 @@ name('*.php') ->exclude('.build') - ->exclude('var') ->in(__DIR__); -$config = new PhpCsFixer\Config(); -return $config +return (new Config()) + ->setFinder($finder) ->setRiskyAllowed(true) ->setRules([ '@DoctrineAnnotation' => true, - '@PSR2' => true, + '@PER' => true, 'header_comment' => [ - 'header' => $headerComment + 'header' => $headerComment, ], + 'no_superfluous_phpdoc_tags' => true, 'array_syntax' => ['syntax' => 'short'], 'blank_line_after_opening_tag' => true, 'braces' => ['allow_single_line_closure' => true], @@ -50,9 +46,11 @@ 'concat_space' => ['spacing' => 'one'], 'declare_equal_normalize' => ['space' => 'none'], 'dir_constant' => true, + 'function_to_constant' => ['functions' => ['get_called_class', 'get_class', 'get_class_this', 'php_sapi_name', 'phpversion', 'pi']], 'function_typehint_space' => true, 'lowercase_cast' => true, 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'], + 'modernize_strpos' => true, 'modernize_types_casting' => true, 'native_function_casing' => true, 'new_with_braces' => true, @@ -67,13 +65,14 @@ 'no_short_bool_cast' => true, 'no_singleline_whitespace_before_semicolons' => true, 'no_superfluous_elseif' => true, - 'no_trailing_comma_in_singleline_array' => true, + 'no_trailing_comma_in_singleline' => true, 'no_unneeded_control_parentheses' => true, 'no_unused_imports' => true, 'no_useless_else' => true, + 'no_useless_nullsafe_operator' => true, 'no_whitespace_in_blank_line' => true, 'ordered_imports' => true, - 'php_unit_construct' => true, + 'php_unit_construct' => ['assertions' => ['assertEquals', 'assertSame', 'assertNotEquals', 'assertNotSame']], 'php_unit_mock_short_will_return' => true, 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], 'phpdoc_no_access' => true, @@ -84,9 +83,10 @@ 'phpdoc_types' => true, 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], 'return_type_declaration' => ['space_before' => 'none'], - 'single_line_comment_style' => false, 'single_quote' => true, + 'single_line_comment_style' => ['comment_types' => ['hash']], 'single_trait_insert_per_statement' => true, - 'whitespace_after_comma_in_array' => true - ]) - ->setFinder($finder); + 'trailing_comma_in_multiline' => ['elements' => ['arrays']], + 'whitespace_after_comma_in_array' => ['ensure_single_space' => true], + 'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false], + ]); diff --git a/Classes/Client/ExtensionSettingException.php b/Classes/Client/ExtensionSettingException.php new file mode 100644 index 0000000..c0599fd --- /dev/null +++ b/Classes/Client/ExtensionSettingException.php @@ -0,0 +1,14 @@ +validateExtConf() === false) { + throw new ExtensionSettingException( + 'Incomplete plesk widget extension settings. See logs for more details', + 1736610959 + ); + } + + $pleskClient = new Client($this->extConf->getHost(), $this->extConf->getPort()); + $pleskClient->setCredentials($this->extConf->getUsername(), $this->extConf->getPassword()); + + return $pleskClient; + } + + private function validateExtConf(): bool + { + if ($this->extConf->getHost() === '') { + $this->logger->error('Plesk host in extension settings can not be empty'); + return false; + } + + if ($this->extConf->getUsername() === '') { + $this->logger->error('Plesk user in extension settings can not be empty'); + return false; + } + + if ($this->extConf->getPassword() === '') { + $this->logger->error('Plesk password in extension settings can not be empty'); + return false; + } + + return true; + } +} diff --git a/Classes/Configuration/ExtConf.php b/Classes/Configuration/ExtConf.php index b8ed9fe..1c479a9 100644 --- a/Classes/Configuration/ExtConf.php +++ b/Classes/Configuration/ExtConf.php @@ -15,34 +15,36 @@ use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\SingletonInterface; -use TYPO3\CMS\Core\Utility\GeneralUtility; /* * This class streamlines all settings from extension settings */ class ExtConf implements SingletonInterface { - protected string $diskUsageType = '%'; + private string $host; - protected string $host = ''; + private int $port; - protected int $port = 8443; + private string $username; - protected string $username = ''; + private string $password; - protected string $password = ''; + private string $diskUsageType; - public function __construct() + private string $domain; + + public function __construct(ExtensionConfiguration $extensionConfiguration) { try { - $extConf = (array)GeneralUtility::makeInstance(ExtensionConfiguration::class) - ->get('plesk_widget'); + $extConf = (array)$extensionConfiguration->get('plesk_widget'); - $this->diskUsageType = trim((string)($extConf['diskUsageType'] ?? '')); $this->host = trim((string)($extConf['host'] ?? '')); $this->port = (int)($extConf['port'] ?? 8443); $this->username = trim((string)($extConf['username'] ?? '')); $this->password = trim((string)($extConf['password'] ?? '')); + + $this->diskUsageType = trim((string)($extConf['diskUsageType'] ?? '%')); + $this->domain = trim((string)($extConf['domain'] ?? '')); } catch (ExtensionConfigurationExtensionNotConfiguredException $extensionConfigurationExtensionNotConfiguredException) { // Do nothing. The values will still be empty. We catch that as Exception just before the first API call } catch (ExtensionConfigurationPathDoesNotExistException $extensionConfigurationPathDoesNotExistException) { @@ -50,11 +52,6 @@ public function __construct() } } - public function getDiskUsageType(): string - { - return $this->diskUsageType; - } - public function getHost(): string { return $this->host; @@ -67,13 +64,21 @@ public function getPort(): int public function getUsername(): string { - //return $this->username; - return ''; + return $this->username; } public function getPassword(): string { - //return $this->password; - return ''; + return $this->password; + } + + public function getDiskUsageType(): string + { + return $this->diskUsageType; + } + + public function getDomain(): string + { + return $this->domain; } } diff --git a/Classes/DataProvider/PleskDataProvider.php b/Classes/DataProvider/PleskDataProvider.php deleted file mode 100644 index c1162b2..0000000 --- a/Classes/DataProvider/PleskDataProvider.php +++ /dev/null @@ -1,177 +0,0 @@ -extConf = $extConf; - $this->pleskClient = new Client($this->extConf->getHost(), $this->extConf->getPort()); - $this->pleskClient->setCredentials($this->extConf->getUsername(), $this->extConf->getPassword()); - } - - public function getChartData(): array - { - return [ - 'labels' => [ - 0 => 'HTTP', - 1 => 'Database', - 2 => 'Logs', - 3 => 'Free' - ], - 'datasets' => [ - [ - 'backgroundColor' => WidgetApi::getDefaultChartColors(), - 'border' => 0, - 'data' => $this->getWebSpaceStatus() - ] - ] - ]; - } - - public function getHosting(): array - { - $hostingInfo = $this->pleskClient->site()->getHosting(null, null); - if ($hostingInfo instanceof HostingInfo) { - return $hostingInfo->properties; - } - - return []; - } - - public function getCustomer(): GeneralInfo - { - $customers = $this->pleskClient->customer()->getAll(); - - return current($customers); - } - - protected function getWebSpaceStatus(): array - { - $diskUsage = $this->getDiskUsage(); - $diskSpace = (int)$this->getLimit('disk_space')->value; - - return [ - 0 => $this->calcDiskUsage( - ($diskUsage->httpdocs + $diskUsage->httpsdocs), - $diskSpace - ), - 1 => $this->calcDiskUsage( - $diskUsage->dbases, - $diskSpace - ), - 2 => $this->calcDiskUsage( - $diskUsage->logs, - $diskSpace - ), - 3 => $this->calcDiskUsage( - ($diskSpace - $diskUsage->httpdocs + $diskUsage->httpsdocs + $diskUsage->dbases + $diskUsage->logs), - $diskSpace - ) - ]; - } - - protected function calcDiskUsage(int $part, int $total = 0): float - { - if ($this->extConf->getDiskUsageType() === '%' && $total !== 0) { - $value = round(100 / $total * $part, 4); - } elseif ($this->extConf->getDiskUsageType() === 'MB') { - $value = round($part / 1024 / 1024, 4); - } elseif ($this->extConf->getDiskUsageType() === 'GB') { - $value = round($part / 1024 / 1024 / 1024, 4); - } else { - $value = (float)$part; - } - - return $value; - } - - protected function getLimit(string $limit): \StefanFroemken\PleskWidget\Plesk\Webspace\Limit - { - return $this->getLimits()->limits[$limit]; - } - - public function getLoginLink(): string - { - // Get external IP address. Works also within DDEV/Docker containers - $externalIpAddress = file_get_contents('http://ipecho.net/plain'); - - if (GeneralUtility::validIP($externalIpAddress)) { - return sprintf( - '%s://%s:%d/enterprise/rsession_init.php?PLESKSESSID=%s&success_redirect_url=%s', - $this->pleskClient->getProtocol() ?: 'https', - $this->pleskClient->getHost(), - $this->pleskClient->getPort(), - $this->pleskClient->server()->createSession($this->extConf->getUsername(), $externalIpAddress), - '/smb/web/view' - ); - } - - return sprintf( - '%s://%s:%d', - $this->pleskClient->getProtocol() ?: 'https', - $this->pleskClient->getHost(), - $this->pleskClient->getPort() - ); - } - - public function getLimits(): \StefanFroemken\PleskWidget\Plesk\Webspace\Limits - { - $packet = $this->pleskClient->getPacket(); - $getTag = $packet->addChild('webspace')->addChild('get'); - $getTag->addChild('filter'); - $getTag->addChild('dataset')->addChild('limits'); - $response = $this->pleskClient->request($packet, \PleskX\Api\Client::RESPONSE_FULL); - - $items = []; - foreach ($response->xpath('//result') as $xmlResult) { - $items[] = new Limits($xmlResult->data->limits); - } - return $items[0]; - } - - public function getSite(): \StefanFroemken\PleskWidget\Plesk\Site - { - $packet = $this->pleskClient->getPacket(); - $getTag = $packet->addChild('site')->addChild('get'); - $getTag->addChild('filter'); - $getTag->addChild('dataset')->addChild('gen_info'); - $response = $this->pleskClient->request($packet, \PleskX\Api\Client::RESPONSE_FULL); - - $sites = []; - foreach ($response->xpath('//result') as $xmlResult) { - $site = new \StefanFroemken\PleskWidget\Plesk\Site($xmlResult); - $site->genInfo = new \StefanFroemken\PleskWidget\Plesk\Site\GeneralInfo($xmlResult->data->gen_info); - $sites[] = $site; - } - return $sites[0]; - } - - public function getDiskUsage(): \PleskX\Api\Struct\Webspace\DiskUsage - { - return $this->pleskClient->webspace()->getDiskUsage(null, null); - } -} diff --git a/Classes/DataProvider/ServerDataProvider.php b/Classes/DataProvider/ServerDataProvider.php new file mode 100644 index 0000000..e1b908c --- /dev/null +++ b/Classes/DataProvider/ServerDataProvider.php @@ -0,0 +1,52 @@ +customer()->getAll(); + + return current($customers); + } + + public function getLoginLink(Client $pleskClient, string $externalIpAddress): string + { + if (GeneralUtility::validIP($externalIpAddress)) { + // Return direct login link + return sprintf( + '%s://%s:%d/enterprise/rsession_init.php?PLESKSESSID=%s&success_redirect_url=%s', + $pleskClient->getProtocol() ?: 'https', + $pleskClient->getHost(), + $pleskClient->getPort(), + $pleskClient->server()->createSession($this->extConf->getUsername(), $externalIpAddress), + '/smb/web/view' + ); + } + + // Return link to login form + return sprintf( + '%s://%s:%d', + $pleskClient->getProtocol() ?: 'https', + $pleskClient->getHost(), + $pleskClient->getPort() + ); + } +} diff --git a/Classes/DataProvider/WebspaceDataProvider.php b/Classes/DataProvider/WebspaceDataProvider.php new file mode 100644 index 0000000..ce42961 --- /dev/null +++ b/Classes/DataProvider/WebspaceDataProvider.php @@ -0,0 +1,118 @@ +pleskClientFactory->create(); + } catch (ExtensionSettingException $e) { + return []; + } + + return [ + 'labels' => [ + 0 => 'HTTP', + 1 => 'Database', + 2 => 'Logs', + 3 => 'Free', + ], + 'datasets' => [ + [ + 'backgroundColor' => WidgetApi::getDefaultChartColors(), + 'border' => 0, + 'data' => $this->getWebSpaceStatus($pleskClient), + ], + ], + ]; + } + + private function getWebSpaceStatus(Client $pleskClient): array + { + $diskUsage = $this->getDiskUsage($pleskClient); + $diskSpace = (int)$this->getLimit('disk_space', $pleskClient)->value; + + return [ + 0 => $this->calcDiskUsage( + ($diskUsage->httpdocs + $diskUsage->httpsdocs), + $diskSpace + ), + 1 => $this->calcDiskUsage( + $diskUsage->dbases, + $diskSpace + ), + 2 => $this->calcDiskUsage( + $diskUsage->logs, + $diskSpace + ), + 3 => $this->calcDiskUsage( + ($diskSpace - $diskUsage->httpdocs + $diskUsage->httpsdocs + $diskUsage->dbases + $diskUsage->logs), + $diskSpace + ), + ]; + } + + private function calcDiskUsage(int $part, int $total = 0): float + { + if ($this->extConf->getDiskUsageType() === '%' && $total !== 0) { + $value = round(100 / $total * $part, 4); + } elseif ($this->extConf->getDiskUsageType() === 'MB') { + $value = round($part / 1024 / 1024, 4); + } elseif ($this->extConf->getDiskUsageType() === 'GB') { + $value = round($part / 1024 / 1024 / 1024, 4); + } else { + $value = (float)$part; + } + + return $value; + } + + private function getLimit(string $limit, Client $pleskClient): \StefanFroemken\PleskWidget\Plesk\Webspace\Limit + { + return $this->getLimits($pleskClient)->limits[$limit]; + } + + private function getLimits(Client $pleskClient): \StefanFroemken\PleskWidget\Plesk\Webspace\Limits + { + $packet = $pleskClient->getPacket(); + $getTag = $packet->addChild('webspace')->addChild('get'); + $getTag->addChild('filter'); + $getTag->addChild('dataset')->addChild('limits'); + $response = $pleskClient->request($packet, Client::RESPONSE_FULL); + + $items = []; + foreach ($response->xpath('//result') as $xmlResult) { + $items[] = new Limits($xmlResult->data->limits); + } + return $items[0]; + } + + private function getDiskUsage(Client $pleskClient): \PleskX\Api\Struct\Webspace\DiskUsage + { + return $pleskClient->webspace()->getDiskUsage(null, null); + } +} diff --git a/Classes/Plesk/Hosting.php b/Classes/Plesk/Hosting.php new file mode 100644 index 0000000..d802a07 --- /dev/null +++ b/Classes/Plesk/Hosting.php @@ -0,0 +1,45 @@ +ipAddresses[] = (string)$xmlIpAddress; + } + foreach ($xmlProperties as $xmlProperty) { + $this->properties[(string)$xmlProperty->name] = (string)$xmlProperty->value; + } + } + + public function getIpAddresses(): array + { + return $this->ipAddresses; + } + + public function getProperties(): array + { + return $this->properties; + } +} diff --git a/Classes/Plesk/Site.php b/Classes/Plesk/Site.php index c30a424..b78cbe7 100644 --- a/Classes/Plesk/Site.php +++ b/Classes/Plesk/Site.php @@ -11,26 +11,48 @@ namespace StefanFroemken\PleskWidget\Plesk; -class Site extends \PleskX\Api\Struct +use PleskX\Api\Client; +use PleskX\Api\Struct\Site\GeneralInfo; + +class Site { - /** @var string */ - public $filterId; + private Client $pleskClient; - /** @var string */ - public $id; + private GeneralInfo $generalInformation; - /** @var string */ - public $status; + public function __construct(Client $pleskClient, GeneralInfo $generalInfo) + { + $this->pleskClient = $pleskClient; + $this->generalInformation = $generalInfo; + } - /** @var \StefanFroemken\PleskWidget\Plesk\Site\GeneralInfo */ - public $genInfo; + public function getGeneralInformation(): GeneralInfo + { + return $this->generalInformation; + } - public function __construct($apiResponse) + public function getHosting(): ?Hosting { - $this->_initScalarProperties($apiResponse, [ - 'filter-id', - 'id', - 'status', + $response = $this->pleskClient->request([ + 'site' => [ + 'get' => [ + 'filter' => [ + 'guid' => $this->generalInformation->guid, + ], + 'dataset' => [ + 'hosting' => null, + ], + ], + ], ]); + + if (!property_exists($response->data->hosting, 'vrt_hst')) { + return null; + } + + return new Hosting( + $response->data->hosting->vrt_hst->ip_address, + $response->data->hosting->vrt_hst->property + ); } } diff --git a/Classes/Plesk/Site/GeneralInfo.php b/Classes/Plesk/Site/GeneralInfo.php deleted file mode 100644 index fd7d271..0000000 --- a/Classes/Plesk/Site/GeneralInfo.php +++ /dev/null @@ -1,60 +0,0 @@ -_initScalarProperties($apiResponse, [ - 'ascii-name', - 'cr_date', - 'description', - 'guid', - 'htype', - 'name', - 'real_size', - 'status', - ]); - - foreach ($apiResponse->dns_ip_address as $address) { - $this->dnsIpAddress[] = $address; - } - } -} diff --git a/Classes/Service/PleskSiteService.php b/Classes/Service/PleskSiteService.php new file mode 100644 index 0000000..81cb057 --- /dev/null +++ b/Classes/Service/PleskSiteService.php @@ -0,0 +1,52 @@ +cache->has('plesk-widget-sites')) { + return $this->cache->get('plesk-widget-sites'); + } + + $sites = []; + foreach ($pleskClient->site()->getAll() as $generalInfo) { + $sites[] = new Site($pleskClient, $generalInfo); + } + + $this->cache->set('plesk-widget-sites', $sites); + + return $sites; + } + + public function getSiteByName(string $name, Client $pleskClient): ?Site + { + foreach ($this->getSites($pleskClient) as $site) { + if ($site->getGeneralInformation()->name === $name) { + return $site; + } + } + + return null; + } +} diff --git a/Classes/Widget/PhpWidget.php b/Classes/Widget/PhpWidget.php new file mode 100644 index 0000000..f2a7da2 --- /dev/null +++ b/Classes/Widget/PhpWidget.php @@ -0,0 +1,73 @@ +request = $request; + } + + public function renderWidgetContent(): string + { + $view = $this->backendViewFactory->create($this->request); + $variables = [ + 'configuration' => $this->configuration, + ]; + + try { + $pleskClient = $this->pleskClientFactory->create(); + + if ($this->extConf->getDomain() === '') { + $variables['error'] = 'You have to select a domain name in extension settings of EXT:plesk-widget.'; + } elseif ($site = $this->pleskSiteService->getSiteByName($this->extConf->getDomain(), $pleskClient)) { + $variables['domain'] = $this->extConf->getDomain(); + $variables['hosting'] = $site->getHosting(); + } else { + $variables['error'] = 'Plesk API can not retrieve domain information for ' . $this->extConf->getDomain(); + } + } catch (ExtensionSettingException $extensionSettingException) { + $variables['error'] = $extensionSettingException->getMessage(); + } + + $view->assignMultiple($variables); + + return $view->render('Widget/Php'); + } + + public function getOptions(): array + { + return $this->options; + } +} diff --git a/Classes/Widget/ServerWidget.php b/Classes/Widget/ServerWidget.php new file mode 100644 index 0000000..66337a8 --- /dev/null +++ b/Classes/Widget/ServerWidget.php @@ -0,0 +1,98 @@ +request = $request; + } + + public function renderWidgetContent(): string + { + $view = $this->backendViewFactory->create($this->request); + $variables = [ + 'configuration' => $this->configuration, + ]; + + try { + $pleskClient = $this->pleskClientFactory->create(); + $externalIpAddress = $this->getExternalIpAddress(); + + $variables['customer'] = $this->dataProvider->getCustomer($pleskClient); + $variables['externalIpAddress'] = $externalIpAddress; + $variables['button'] = $this->getButtonProvider($pleskClient, $externalIpAddress); + + if ( + $this->extConf->getDomain() + && ($site = $this->pleskSiteService->getSiteByName($this->extConf->getDomain(), $pleskClient)) + && $ipAddresses = $site->getHosting()->getIpAddresses() + ) { + $variables['ipAddresses'] = $ipAddresses; + } + } catch (ExtensionSettingException $extensionSettingException) { + $variables['error'] = $extensionSettingException->getMessage(); + } + + $view->assignMultiple($variables); + + return $view->render('Widget/Server'); + } + + protected function getButtonProvider(Client $pleskClient, string $externalIpAddress): ButtonProvider + { + return GeneralUtility::makeInstance( + ButtonProvider::class, + 'Login to Plesk', + $this->dataProvider->getLoginLink($pleskClient, $externalIpAddress), + '_blank' + ); + } + + private function getExternalIpAddress(): string + { + // Get external IP address. Works also within DDEV/Docker containers + return file_get_contents('http://ipecho.net/plain'); + } + + public function getOptions(): array + { + return $this->options; + } +} diff --git a/Classes/Widget/WebspaceWidget.php b/Classes/Widget/WebspaceWidget.php new file mode 100644 index 0000000..1c47fe8 --- /dev/null +++ b/Classes/Widget/WebspaceWidget.php @@ -0,0 +1,96 @@ +request = $request; + } + + public function renderWidgetContent(): string + { + $view = $this->backendViewFactory->create($this->request); + + $view->assign('configuration', $this->configuration); + + return $view->render('Widget/Webspace'); + } + + public function getEventData(): array + { + return [ + 'graphConfig' => [ + 'type' => 'doughnut', + 'options' => [ + 'maintainAspectRatio' => false, + 'plugins' => [ + 'legend' => [ + 'display' => true, + 'position' => 'bottom', + ], + 'title' => [ + 'display' => true, + 'text' => 'Usage in ' . $this->extConf->getDiskUsageType(), + ], + 'tooltip' => [ + 'enabled' => true, + ], + ], + 'cutoutPercentage' => 60, + ], + 'data' => $this->dataProvider->getChartData(), + ], + ]; + } + + public function getCssFiles(): array + { + return []; + } + + public function getJavaScriptModuleInstructions(): array + { + return [ + JavaScriptModuleInstruction::create('@typo3/dashboard/contrib/chartjs.js'), + JavaScriptModuleInstruction::create('@typo3/dashboard/chart-initializer.js'), + ]; + } + + public function getOptions(): array + { + return $this->options; + } +} diff --git a/Classes/Widgets/Webspace.php b/Classes/Widgets/Webspace.php deleted file mode 100644 index cd9a513..0000000 --- a/Classes/Widgets/Webspace.php +++ /dev/null @@ -1,134 +0,0 @@ -configuration = $configuration; - $this->view = $view; - $this->dataProvider = $dataProvider; - $this->extConf = $extConf; - } - - public function renderWidgetContent(): string - { - try { - if ($this->extConf->getUsername() === '' || $this->extConf->getPassword() === '') { - $this->hasError = true; - $variables = [ - 'error' => 'No username or password given. Please configure authentication in extension settings of EXT:plesk_widget' - ]; - } else { - $variables = [ - 'configuration' => $this->configuration, - 'customer' => $this->dataProvider->getCustomer(), - 'hosting' => $this->dataProvider->getHosting(), - 'site' => $this->dataProvider->getSite(), - 'button' => $this->getButtonProvider() - ]; - } - } catch (\Exception $exception) { - $this->hasError = true; - $variables = [ - 'error' => $exception->getMessage() - ]; - } - - $this->view->setTemplate('Widget/Webspace'); - $this->view->assign('configuration', $this->configuration); - $this->view->assignMultiple($variables); - - return $this->view->render(); - } - - public function getEventData(): array - { - if ($this->hasError) { - $data = '{}'; - } else { - $data = $this->dataProvider->getChartData(); - } - - return [ - 'graphConfig' => [ - 'type' => 'doughnut', - 'options' => [ - //'maintainAspectRatio' => false, - 'legend' => [ - 'display' => true, - 'position' => 'bottom' - ], - 'tooltips' => [ - 'enabled' => true - ], - 'title' => [ - 'display' => true, - 'text' => 'Usage in ' . $this->extConf->getDiskUsageType() - ] - ], - 'data' => $data, - ], - ]; - } - - protected function getButtonProvider(): ButtonProvider - { - return GeneralUtility::makeInstance( - ButtonProvider::class, - 'Login to Plesk', - $this->dataProvider->getLoginLink(), - '_blank' - ); - } - - public function getCssFiles(): array - { - return [ - 'EXT:dashboard/Resources/Public/Css/Contrib/chart.css' - ]; - } - - public function getRequireJsModules(): array - { - return [ - 'TYPO3/CMS/Dashboard/Contrib/chartjs', - 'TYPO3/CMS/Dashboard/ChartInitializer', - ]; - } -} diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 0d7a146..848e1ec 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -6,12 +6,12 @@ services: StefanFroemken\PleskWidget\: resource: '../Classes/*' + exclude: '../Classes/Widget/*' dashboard.widget.plesk.webspace: - class: 'StefanFroemken\PleskWidget\Widgets\Webspace' + class: 'StefanFroemken\PleskWidget\Widget\WebspaceWidget' arguments: - $view: '@dashboard.views.widget' - $dataProvider: '@StefanFroemken\PleskWidget\DataProvider\PleskDataProvider' + $dataProvider: '@StefanFroemken\PleskWidget\DataProvider\WebspaceDataProvider' tags: - name: dashboard.widget identifier: 'plesk.webspace' @@ -20,4 +20,31 @@ services: description: 'Show used and available diskspace' iconIdentifier: 'content-widget-rss' height: 'medium' - width: 'medium' + + dashboard.widget.plesk.server: + class: 'StefanFroemken\PleskWidget\Widget\ServerWidget' + arguments: + $dataProvider: '@StefanFroemken\PleskWidget\DataProvider\ServerDataProvider' + tags: + - name: dashboard.widget + identifier: 'plesk.server' + groupNames: 'systemInfo' + title: 'Plesk Server Information' + description: 'Show server related information and login link' + iconIdentifier: 'content-widget-rss' + height: 'medium' + + dashboard.widget.plesk.php: + class: 'StefanFroemken\PleskWidget\Widget\PhpWidget' + tags: + - name: dashboard.widget + identifier: 'plesk.php' + groupNames: 'systemInfo' + title: 'Plesk PHP Settings' + description: 'Show PHP related information' + iconIdentifier: 'content-widget-rss' + height: 'medium' + + StefanFroemken\PleskWidget\Service\PleskSiteService: + arguments: + $cache: '@cache.runtime' diff --git a/Configuration/page.tsconfig b/Configuration/page.tsconfig new file mode 100644 index 0000000..3ba23c1 --- /dev/null +++ b/Configuration/page.tsconfig @@ -0,0 +1 @@ +templates.typo3/cms-dashboard.1736608068 = stefanfroemken/plesk-widget:Resources/Private diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Client.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Client.php index 80e83be..7fb24d9 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Client.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Client.php @@ -1,5 +1,11 @@ _version; $content = ""; - $content .= ''; + $content .= ''; return new SimpleXMLElement($content); } @@ -163,9 +167,9 @@ public function request($request, $mode = self::RESPONSE_SHORT) } } - if ('sdk' == $this->_protocol) { - $version = ('' == $this->_version) ? null : $this->_version; - $requestXml = new SimpleXMLElement((string) $request); + if ($this->_protocol == 'sdk') { + $version = ($this->_version == '') ? null : $this->_version; + $requestXml = new SimpleXMLElement((string)$request); $xml = \pm_ApiRpc::getService($version)->call($requestXml->children()[0]->asXml(), $this->_login); } else { $xml = $this->_performHttpRequest($request); @@ -175,7 +179,7 @@ public function request($request, $mode = self::RESPONSE_SHORT) ? call_user_func($this->_verifyResponseCallback, $xml) : $this->_verifyResponse($xml); - return (self::RESPONSE_FULL == $mode) ? $xml : $xml->xpath('//result')[0]; + return ($mode == self::RESPONSE_FULL) ? $xml : $xml->xpath('//result')[0]; } /** @@ -199,13 +203,13 @@ private function _performHttpRequest($request) curl_setopt($curl, CURLOPT_HTTPHEADER, $this->_getHeaders()); curl_setopt($curl, CURLOPT_POSTFIELDS, $request); - if ('' !== $this->_proxy) { + if ($this->_proxy !== '') { curl_setopt($curl, CURLOPT_PROXY, $this->_proxy); } $result = curl_exec($curl); - if (false === $result) { + if ($result === false) { throw new Client\Exception(curl_error($curl), curl_errno($curl)); } @@ -219,7 +223,6 @@ private function _performHttpRequest($request) /** * Perform multiple API requests using single HTTP request. * - * @param $requests * @param int $mode * * @throws Client\Exception @@ -233,21 +236,20 @@ public function multiRequest($requests, $mode = self::RESPONSE_SHORT) foreach ($requests as $request) { if ($request instanceof SimpleXMLElement) { throw new Client\Exception('SimpleXML type of request is not supported for multi requests.'); - } else { - if (is_array($request)) { - $request = $this->_arrayToXml($request, $requestXml)->asXML(); - } elseif (preg_match('/^[a-z]/', $request)) { - $this->_expandRequestShortSyntax($request, $requestXml); - } } + if (is_array($request)) { + $request = $this->_arrayToXml($request, $requestXml)->asXML(); + } elseif (preg_match('/^[a-z]/', $request)) { + $this->_expandRequestShortSyntax($request, $requestXml); + } + $responses[] = $this->request($request); } - if ('sdk' == $this->_protocol) { + if ($this->_protocol == 'sdk') { throw new Client\Exception('Multi requests are not supported via SDK.'); - } else { - $responseXml = $this->_performHttpRequest($requestXml->asXML()); } + $responseXml = $this->_performHttpRequest($requestXml->asXML()); $responses = []; foreach ($responseXml->children() as $childNode) { @@ -259,7 +261,7 @@ public function multiRequest($requests, $mode = self::RESPONSE_SHORT) $dom->documentElement->appendChild($childDomNode); $response = simplexml_load_string($dom->saveXML()); - $responses[] = (self::RESPONSE_FULL == $mode) ? $response : $response->xpath('//result')[0]; + $responses[] = ($mode == self::RESPONSE_FULL) ? $response : $response->xpath('//result')[0]; } return $responses; @@ -296,13 +298,13 @@ protected function _getHeaders() */ protected function _verifyResponse($xml) { - if ($xml->system && $xml->system->status && 'error' == (string) $xml->system->status) { - throw new Exception((string) $xml->system->errtext, (int) $xml->system->errcode); + if ($xml->system && $xml->system->status && (string)$xml->system->status == 'error') { + throw new Exception((string)$xml->system->errtext, (int)$xml->system->errcode); } if ($xml->xpath('//status[text()="error"]') && $xml->xpath('//errcode') && $xml->xpath('//errtext')) { - $errorCode = (int) $xml->xpath('//errcode')[0]; - $errorMessage = (string) $xml->xpath('//errtext')[0]; + $errorCode = (int)$xml->xpath('//errcode')[0]; + $errorMessage = (string)$xml->xpath('//errtext')[0]; throw new Exception($errorMessage, $errorCode); } @@ -312,7 +314,6 @@ protected function _verifyResponse($xml) * Expand short syntax (some.method.call) into full XML representation. * * @param string $request - * @param SimpleXMLElement $xml * * @return string */ @@ -327,7 +328,7 @@ protected function _expandRequestShortSyntax($request, SimpleXMLElement $xml) if ($part !== $lastParts) { $node = $node->addChild($name); } else { - $node->{$name} = (string) $value; + $node->{$name} = (string)$value; } } @@ -337,8 +338,6 @@ protected function _expandRequestShortSyntax($request, SimpleXMLElement $xml) /** * Convert array to XML representation. * - * @param array $array - * @param SimpleXMLElement $xml * @param string $parentEl * * @return SimpleXMLElement @@ -350,7 +349,7 @@ protected function _arrayToXml(array $array, SimpleXMLElement $xml, $parentEl = if (is_array($value)) { $this->_arrayToXml($value, $this->_isAssocArray($value) ? $xml->addChild($el) : $xml, $el); } else { - $xml->{$el} = (string) $value; + $xml->{$el} = (string)$value; } } @@ -358,8 +357,6 @@ protected function _arrayToXml(array $array, SimpleXMLElement $xml, $parentEl = } /** - * @param array $array - * * @return bool */ protected function _isAssocArray(array $array) @@ -375,7 +372,7 @@ protected function _isAssocArray(array $array) protected function _getOperator($name) { if (!isset($this->_operatorsCache[$name])) { - $className = '\\PleskX\\Api\\Operator\\'.$name; + $className = '\\PleskX\\Api\\Operator\\' . $name; $this->_operatorsCache[$name] = new $className($this); } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Client/Exception.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Client/Exception.php index 0d26d30..66860c5 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Client/Exception.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Client/Exception.php @@ -1,11 +1,15 @@ _client = $client; if (is_null($this->_wrapperTag)) { - $classNameParts = explode('\\', get_class($this)); + $classNameParts = explode('\\', static::class); $this->_wrapperTag = end($classNameParts); $this->_wrapperTag = strtolower(preg_replace('/([a-z])([A-Z])/', '\1-\2', $this->_wrapperTag)); } @@ -62,7 +68,7 @@ protected function _delete($field, $value, $deleteMethodName = 'del') ], ]); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } /** @@ -70,9 +76,6 @@ protected function _delete($field, $value, $deleteMethodName = 'del') * @param string $infoTag * @param string|null $field * @param int|string|null $value - * @param callable|null $filter - * - * @return mixed */ protected function _getItems($structClass, $infoTag, $field = null, $value = null, callable $filter = null) { diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Aps.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Aps.php index 9f4bde3..54f9600 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Aps.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Aps.php @@ -1,8 +1,12 @@ addChild($this->_wrapperTag)->addChild($command); foreach ($properties as $name => $value) { - if (false !== strpos($value, '&')) { + if (str_contains($value, '&')) { $info->$name = $value; continue; } @@ -50,15 +55,13 @@ private function _process($command, array $properties) } /** - * @param array $properties - * * @return bool */ public function updateUser(array $properties) { $response = $this->_process('set-db-user', $properties); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DatabaseServer.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DatabaseServer.php index fc338ea..751967b 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DatabaseServer.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DatabaseServer.php @@ -1,5 +1,11 @@ request('get-supported-types'); - return (array) $response->type; + return (array)$response->type; } /** @@ -61,7 +67,7 @@ private function _get($field = null, $value = null) $items = []; foreach ($response->xpath('//result') as $xmlResult) { $item = new Struct\Info($xmlResult->data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Dns.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Dns.php index 30920a7..91ef5ec 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Dns.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Dns.php @@ -1,5 +1,11 @@ xpath('//result') as $xmlResult) { $item = new Struct\Info($xmlResult->data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } @@ -106,7 +111,6 @@ public function delete($field, $value) /** * Delete multiply records by one request. * - * @param array $recordIds * * @return \PleskX\Api\XmlResponse[] */ diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DnsTemplate.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DnsTemplate.php index 18e6634..40391af 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DnsTemplate.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/DnsTemplate.php @@ -1,5 +1,11 @@ xpath('//result') as $xmlResult) { $item = new Struct\Info($xmlResult->data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } @@ -83,6 +87,6 @@ public function delete($field, $value) $response = $this->_client->request($packet); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/EventLog.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/EventLog.php index 6b7325a..210f442 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/EventLog.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/EventLog.php @@ -1,5 +1,11 @@ request('get-last-id')->getValue('id'); + return (int)$this->request('get-last-id')->getValue('id'); } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ip.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ip.php index 4bc87f7..91550c0 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ip.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ip.php @@ -1,5 +1,11 @@ _client->request($packet, \PleskX\Api\Client::RESPONSE_FULL); foreach ($response->locale->get->result as $localeInfo) { - $locales[(string) $localeInfo->info->id] = new Struct\Info($localeInfo->info); + $locales[(string)$localeInfo->info->id] = new Struct\Info($localeInfo->info); } return !is_null($id) ? reset($locales) : $locales; diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/LogRotation.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/LogRotation.php index ef0b60e..e745205 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/LogRotation.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/LogRotation.php @@ -1,8 +1,12 @@ {$field} = $value; $response = $this->_client->request($packet); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/PhpHandler.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/PhpHandler.php index 66f1ec1..7ab15ed 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/PhpHandler.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/PhpHandler.php @@ -1,5 +1,11 @@ xpath('//result') as $xmlResult) { $item = new Struct\GeneralInfo($xmlResult->data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ResellerPlan.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ResellerPlan.php index 17d533f..b79f591 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ResellerPlan.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ResellerPlan.php @@ -1,8 +1,12 @@ _client->getPacket(); $createTag = $packet->addChild($this->_wrapperTag)->addChild('create'); - if ('' !== $ipAddress) { + if ($ipAddress !== '') { $createTag->addChild('ip_address', $ipAddress); } - if ('' !== $description) { + if ($description !== '') { $createTag->addChild('description', $description); } $response = $this->_client->request($packet); - return (string) $response->key; + return (string)$response->key; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Server.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Server.php index 65cbae6..420a951 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Server.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Server.php @@ -1,5 +1,11 @@ addChild($this->_wrapperTag)->addChild('get_protos'); $response = $this->_client->request($packet); - return (array) $response->protos->proto; + return (array)$response->protos->proto; } public function getGeneralInfo() @@ -43,7 +49,7 @@ public function getKeyInfo() $keyInfoXml = $this->_getInfo('key'); foreach ($keyInfoXml->property as $property) { - $keyInfo[(string) $property->name] = (string) $property->value; + $keyInfo[(string)$property->name] = (string)$property->value; } return $keyInfo; @@ -58,7 +64,7 @@ public function getComponents() $componentsXml = $this->_getInfo('components'); foreach ($componentsXml->component as $component) { - $components[(string) $component->name] = (string) $component->version; + $components[(string)$component->name] = (string)$component->version; } return $components; @@ -73,10 +79,10 @@ public function getServiceStates() $statesXml = $this->_getInfo('services_state'); foreach ($statesXml->srv as $service) { - $states[(string) $service->id] = [ - 'id' => (string) $service->id, - 'title' => (string) $service->title, - 'state' => (string) $service->state, + $states[(string)$service->id] = [ + 'id' => (string)$service->id, + 'title' => (string)$service->title, + 'state' => (string)$service->state, ]; } @@ -97,7 +103,7 @@ public function getShells() $shellsXml = $this->_getInfo('shells'); foreach ($shellsXml->shell as $shell) { - $shells[(string) $shell->name] = (string) $shell->path; + $shells[(string)$shell->name] = (string)$shell->path; } return $shells; @@ -110,7 +116,7 @@ public function getNetworkInterfaces() { $interfacesXml = $this->_getInfo('interfaces'); - return (array) $interfacesXml->interface; + return (array)$interfacesXml->interface; } public function getStatistics() @@ -127,7 +133,7 @@ public function getSiteIsolationConfig() $configXml = $this->_getInfo('site-isolation-config'); foreach ($configXml->property as $property) { - $config[(string) $property->name] = (string) $property->value; + $config[(string)$property->name] = (string)$property->value; } return $config; @@ -154,7 +160,7 @@ public function createSession($login, $clientIp) $dataNode->addChild('source_server'); $response = $this->_client->request($packet); - return (string) $response->id; + return (string)$response->id; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ServicePlan.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ServicePlan.php index 41fa075..e440693 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ServicePlan.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/ServicePlan.php @@ -1,5 +1,11 @@ request('get'); foreach ($response->session as $sessionInfo) { - $sessions[(string) $sessionInfo->id] = new Struct\Info($sessionInfo); + $sessions[(string)$sessionInfo->id] = new Struct\Info($sessionInfo); } return $sessions; @@ -31,6 +37,6 @@ public function terminate($sessionId) { $response = $this->request("terminate.session-id=$sessionId"); - return 'ok' === (string) $response->status; + return (string)$response->status === 'ok'; } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Site.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Site.php index f92eae6..8949793 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Site.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Site.php @@ -1,5 +1,11 @@ data); - $item->id = (int) $xmlResult->id; + $item->id = (int)$xmlResult->id; $items[] = $item; } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ui.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ui.php index 187fcd2..01bab45 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ui.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/Ui.php @@ -1,5 +1,11 @@ _client->request($packet); - return (int) $response->id; + return (int)$response->id; } /** diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/VirtualDirectory.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/VirtualDirectory.php index 1c2ae8b..79ed025 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/VirtualDirectory.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Operator/VirtualDirectory.php @@ -1,5 +1,11 @@ getDocComment(); $propertyType = preg_replace('/^.+ @var ([a-z]+) .+$/', '\1', $docBlock); - if ('string' == $propertyType) { - $value = (string) $value; - } elseif ('int' == $propertyType) { - $value = (int) $value; - } elseif ('bool' == $propertyType) { - $value = in_array((string) $value, ['true', 'on', 'enabled']); + if ($propertyType == 'string') { + $value = (string)$value; + } elseif ($propertyType == 'int') { + $value = (int)$value; + } elseif ($propertyType == 'bool') { + $value = in_array((string)$value, ['true', 'on', 'enabled']); } else { throw new \Exception("Unknown property type '$propertyType'."); } @@ -56,7 +61,7 @@ protected function _initScalarProperties($apiResponse, array $properties) */ private function _underToCamel($under) { - $under = '_'.str_replace('_', ' ', strtolower($under)); + $under = '_' . str_replace('_', ' ', strtolower($under)); return ltrim(str_replace(' ', '', ucwords($under)), '_'); } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Certificate/Info.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Certificate/Info.php index 7689bcb..129ac11 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Certificate/Info.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Certificate/Info.php @@ -1,5 +1,11 @@ permissions = []; foreach ($apiResponse->permissions->permission as $permissionInfo) { - $this->permissions[(string) $permissionInfo->name] = (string) $permissionInfo->value; + $this->permissions[(string)$permissionInfo->name] = (string)$permissionInfo->value; } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Reseller/Info.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Reseller/Info.php index a1b6bf8..2df7d10 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Reseller/Info.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Reseller/Info.php @@ -1,5 +1,11 @@ diskSpace = []; foreach ($apiResponse->diskspace as $disk) { - $this->diskSpace[(string) $disk->device->name] = new Statistics\DiskSpace($disk->device); + $this->diskSpace[(string)$disk->device->name] = new Statistics\DiskSpace($disk->device); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Server/Statistics/DiskSpace.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Server/Statistics/DiskSpace.php index 1f21655..ae964b8 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Server/Statistics/DiskSpace.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Server/Statistics/DiskSpace.php @@ -1,5 +1,11 @@ vrt_hst->property as $property) { - $this->properties[(string) $property->name] = (string) $property->value; + $this->properties[(string)$property->name] = (string)$property->value; } $this->_initScalarProperties($apiResponse->vrt_hst, [ 'ip_address', diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Site/Info.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Site/Info.php index ea69dd9..df17f1f 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Site/Info.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Site/Info.php @@ -1,5 +1,11 @@ property as $propertyInfo) { - $this->properties[(string) $propertyInfo->name] = (string) $propertyInfo->value; + $this->properties[(string)$propertyInfo->name] = (string)$propertyInfo->value; } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Ui/CustomButton.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Ui/CustomButton.php index ed7a712..417951a 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Ui/CustomButton.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Ui/CustomButton.php @@ -1,5 +1,11 @@ dns_ip_address as $ip) { - $this->ipAddresses[] = (string) $ip; + $this->ipAddresses[] = (string)$ip; } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/HostingPropertyInfo.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/HostingPropertyInfo.php index 7922d18..d5024a9 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/HostingPropertyInfo.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/HostingPropertyInfo.php @@ -1,5 +1,11 @@ limits = []; foreach ($apiResponse->descriptor->property as $propertyInfo) { - $this->limits[(string) $propertyInfo->name] = new LimitInfo($propertyInfo); + $this->limits[(string)$propertyInfo->name] = new LimitInfo($propertyInfo); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/LimitInfo.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/LimitInfo.php index b8af3b6..2cb0614 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/LimitInfo.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/LimitInfo.php @@ -1,5 +1,11 @@ limits = []; foreach ($apiResponse->limit as $limit) { - $this->limits[(string) $limit->name] = new Limit($limit); + $this->limits[(string)$limit->name] = new Limit($limit); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionDescriptor.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionDescriptor.php index bc333ba..106611a 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionDescriptor.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionDescriptor.php @@ -1,5 +1,11 @@ permissions = []; foreach ($apiResponse->descriptor->property as $propertyInfo) { - $this->permissions[(string) $propertyInfo->name] = new PermissionInfo($propertyInfo); + $this->permissions[(string)$propertyInfo->name] = new PermissionInfo($propertyInfo); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionInfo.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionInfo.php index 9414a31..34b66b5 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionInfo.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PermissionInfo.php @@ -1,5 +1,11 @@ properties = []; foreach ($apiResponse->webspace->get->result->data->{'php-settings'}->setting as $setting) { - $this->properties[(string) $setting->name] = (string) $setting->value; + $this->properties[(string)$setting->name] = (string)$setting->value; } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PhysicalHostingDescriptor.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PhysicalHostingDescriptor.php index 1aff2f7..e9b0af9 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PhysicalHostingDescriptor.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/Struct/Webspace/PhysicalHostingDescriptor.php @@ -1,5 +1,11 @@ properties = []; foreach ($apiResponse->descriptor->property as $propertyInfo) { - $this->properties[(string) $propertyInfo->name] = new HostingPropertyInfo($propertyInfo); + $this->properties[(string)$propertyInfo->name] = new HostingPropertyInfo($propertyInfo); } } } diff --git a/Resources/Private/Php/plesk/api-php-lib/src/Api/XmlResponse.php b/Resources/Private/Php/plesk/api-php-lib/src/Api/XmlResponse.php index 687b6d8..34f7560 100644 --- a/Resources/Private/Php/plesk/api-php-lib/src/Api/XmlResponse.php +++ b/Resources/Private/Php/plesk/api-php-lib/src/Api/XmlResponse.php @@ -1,5 +1,11 @@ xpath('//'.$node)[0]; + return (string)$this->xpath('//' . $node)[0]; } } diff --git a/Resources/Private/Templates/Dashboard/Widget/Webspace.html b/Resources/Private/Templates/Dashboard/Widget/Webspace.html deleted file mode 100644 index 9681b75..0000000 --- a/Resources/Private/Templates/Dashboard/Widget/Webspace.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - -
- - -
{error}
-
- -
-
- -
-
-
-
-
Owner
-
{customer.personalName}
-
-
-
IP Address
- -
{ipAddress}
-
-
-
-
Hosting
-
PHP Handler: {hosting.php_handler_id}
-
PHP memory: {hosting.memory_limit}
-
PHP exec. time: {hosting.max_execution_time}
-
PHP max. upload: {hosting.upload_max_filesize}
-
-
-
-
-
-
- - - - - diff --git a/Resources/Private/Templates/Widget/Php.html b/Resources/Private/Templates/Widget/Php.html new file mode 100644 index 0000000..246d120 --- /dev/null +++ b/Resources/Private/Templates/Widget/Php.html @@ -0,0 +1,39 @@ + + + + + + + {error} + +

{domain}

+
+
max_execution_time
+
{hosting.properties.max_execution_time}
+
+
+
open_basedir
+
{hosting.properties.open_basedir}
+
+
+
memory_limit
+
{hosting.properties.memory_limit}
+
+
+
post_max_size
+
{hosting.properties.post_max_size}
+
+
+
upload_max_filesize
+
{hosting.properties.upload_max_filesize}
+
+
+
+
+ + + + + diff --git a/Resources/Private/Templates/Widget/Server.html b/Resources/Private/Templates/Widget/Server.html new file mode 100644 index 0000000..ec78e5d --- /dev/null +++ b/Resources/Private/Templates/Widget/Server.html @@ -0,0 +1,32 @@ + + + + + + + {error} + +
+
Owner
+
{customer.personalName}
+
+
+
Server IP Address
+ +
{ipAddress}
+
+
+ +
Public IP Address
+
{externalIpAddress}
+
+
+
+
+ + + + + diff --git a/Resources/Private/Templates/Widget/Webspace.html b/Resources/Private/Templates/Widget/Webspace.html new file mode 100644 index 0000000..043c825 --- /dev/null +++ b/Resources/Private/Templates/Widget/Webspace.html @@ -0,0 +1,22 @@ + + + + + + + {error} + + +
+ +
+
+
+
+ + + + + diff --git a/composer.json b/composer.json index c06da0a..871ee1c 100644 --- a/composer.json +++ b/composer.json @@ -18,13 +18,19 @@ "source": "https://github.com/froemken/plesk_widget" }, "require": { - "php": "^7.4 || ^8.0", - "typo3/cms-core": "^11.5.41 || ^12.4.24", - "typo3/cms-dashboard": "^11.5.41 || ^12.4.24", + "typo3/cms-core": "^12.4.24", + "typo3/cms-dashboard": "^12.4.24", "plesk/api-php-lib": "1.1.2" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.4" + "friendsofphp/php-cs-fixer": "^3.14", + "phpunit/phpunit": "^9.6", + "roave/security-advisories": "dev-latest", + "typo3/coding-standards": "^0.6", + "typo3/testing-framework": "^7.0.2" + }, + "replace": { + "typo3-ter/plesk-widget": "self.version" }, "autoload": { "psr-4": { @@ -33,25 +39,19 @@ } }, "config": { - "vendor-dir": ".build/vendor" + "sort-packages": true, + "vendor-dir": ".Build/vendor", + "bin-dir": ".Build/bin", + "allow-plugins": { + "typo3/class-alias-loader": true, + "typo3/cms-composer-installers": true + } }, "extra": { "typo3/cms": { "extension-key": "plesk_widget", - "app-dir": ".build", - "web-dir": ".build/public" + "app-dir": ".Build", + "web-dir": ".Build/public" } - }, - "scripts": { - "php:fix": ".build/vendor/bin/php-cs-fixer --config=Build/.php_cs.php fix Classes", - "ci:php:lint": "find *.php Classes Configuration -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l", - "ci:php:fixer": ".build/vendor/bin/php-cs-fixer --config=Build/.php_cs.php fix --dry-run -v --show-progress=dots --diff Classes", - "link-extension": [ - "@php -r 'is_dir($extFolder=__DIR__.\"/.build/public/typo3conf/ext/\") || mkdir($extFolder, 0777, true);'", - "@php -r 'file_exists($extFolder=__DIR__.\"/.build/public/typo3conf/ext/plesk_widget\") || symlink(__DIR__,$extFolder);'" - ], - "post-autoload-dump": [ - "@link-extension" - ] } } diff --git a/ext_conf_template.txt b/ext_conf_template.txt index f67fe05..05676b5 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -1,10 +1,12 @@ -# cat=basic; type=options[Percent (%)=%,MegaByte (MB)=MB,GigaByte (GB)=GB]; label=Disk usage type: Show disk usage in percent or in Mega- or GigaByte? -diskUsageType = % -# cat=basic; type=string; label=Plesk host: Insert without scheme, port or path +# cat=plesk; type=string; label=Plesk host: Insert without scheme, port or path host = -# cat=basic; type=int; label=Plesk port: Plesk default port is 8443 +# cat=plesk; type=int; label=Plesk port: Plesk default port is 8443 port = 8443 -# cat=basic; type=string; label=Plesk username +# cat=plesk; type=string; label=Plesk username username = -# cat=basic; type=string; label=Plesk password +# cat=plesk; type=string; label=Plesk password password = +# cat=basic; type=options[Percent (%)=%,MegaByte (MB)=MB,GigaByte (GB)=GB]; label=Disk usage type: Show disk usage in percent or in Mega- or GigaByte? +diskUsageType = % +# cat=basic; type=string; label=Domain: Enter domain name to show info about. Without www and without scheme (https://) +domain = diff --git a/ext_emconf.php b/ext_emconf.php index c9e3490..d1a3757 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -1,5 +1,12 @@ 'Plesk Widget', 'description' => 'Shows information about your plesk based customer control center', @@ -9,11 +16,11 @@ 'author_company' => '', 'state' => 'stable', 'clearCacheOnLoad' => 0, - 'version' => '1.2.1', + 'version' => '2.0.0', 'constraints' => [ 'depends' => [ - 'typo3' => '11.5.41-12.4.99', - 'dashboard' => '11.5.41-12.4.99', + 'typo3' => '12.4.24', + 'dashboard' => '12.4.24', ], 'conflicts' => [ ],