diff --git a/.gitignore b/.gitignore index 450b9404dd50a9050b9dad42d6f4363ae13699d9..a30b4b373ec285d52d494ebb90c11bda588c4f3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,21 @@ nbproject +/config/sshkeys/* +/config/projects/* /config/config_custom.php -/config/sshkeys/git@gitlab.iml.unibe.ch.pub -/config/sshkeys/git@gitlab.iml.unibe.ch -/config/projects/mmmu-sf2.json -/config/projects/mmmu-sf2.json.ok -/config/projects/doccom.json -/config/projects/imlplayer.json_1.ok -/config/projects/mmmu-sf2.json_1.ok -/config/projects/ci.json_1.ok -/config/projects/doccom.json_1.ok -/config/projects/ci.json.ok -/config/projects/imlplayer.json -/config/projects/imlplayer.json.ok -/config/projects/doccom.json.ok -/config/projects/ci.json +/config/inc_user2projects.php +/data/imldeployment/packages/* +/data/.ssh/known_hosts /data/imldeployment/data/database/logs.db +/data/imldeployment/data/database/logs2.db /data/imldeployment/data/projects/*.json* +/data/imldeployment/data/sshkeys/* + /public_html/deployment/dummy.db -/public_html/deployment/classes/ramdb.class.php /public_html/~cache/ -/public_html/deployment/classes/spooler-handler.class.php -/public_html/deployment/pages/act_jstest.php -/public_html/deployment/classes/html-adminltetest.tpl.php -/public_html/deployment/adminlte/ -/config/inc_user2projects.php -/shellscripts/spooler/ +/shellscripts/ /public_html/valuestore/data/versioncache.db /public_html/vendor/medoo/ /config/_inc_projects_config.php /config/inc_projects_config.php /config/inc_user2roles.php /.vscode/ -data/imldeployment/packages/* \ No newline at end of file diff --git a/config/config_custom.php.dist b/config/config_custom.php.dist index b8ea8520b2b2a29e848145447f904319e5d89a3b..50c09d5c8769a35144225b26528d2078b8234068 100644 --- a/config/config_custom.php.dist +++ b/config/config_custom.php.dist @@ -91,6 +91,7 @@ return [ 'rollout'=>[ 'default'=>[], + /* 'ssh'=>[ 'user'=>'imldeployment', 'privatekey'=>'', @@ -99,13 +100,13 @@ return [ 'command'=>'/usr/local/bin/puppetrun.sh', ], 'awx'=>[ - 'url'=>'https://awx.sys.iml.unibe.ch/api/v2', // no ending "/" - 'user'=>'api-ci', - 'password'=>'awRSbdB2rkViaBXBKOvtr11DEoZJSqHceih1hEE4awrjIO1wuArKu85WmetsRp63', + 'url'=>'https://awx.example.com/api/v2', // no ending "/" + 'user'=>'ciserver', + 'password'=>'ciserver', 'jobtemplate'=>'36', 'tags'=>'rollout', - // 'ignore-ssl-error'=>false, ], + */ ], ], @@ -128,6 +129,42 @@ return [ ], ], ], + + // ---------------------------------------------------------------------- + // APPMONITOR + // see <https://os-docs.iml.unibe.ch/appmonitor/> + // ---------------------------------------------------------------------- + + "appmonitor" => [ + + // notification + + // email notification for this application + /* + "email" => [ + "sysadmin@example.com" + ], + */ + // email notification for this application + /* + "slack" => [ + ["#sysadmins", "https://hooks.slack.com/services/AAA/BBB/CCC" ] + ], + */ + + // limit access to appmonitor client to specific IP addresses + /* + "ip" => [ + '127.0.0.1', + '::1', + '10.0.2.2', + ], + */ + + // limit access to appmonitor client to specific token + // "token" => ["token", "1234567890"], + + // ---------------------------------------------------------------------- ]; \ No newline at end of file diff --git a/config/inc_projects_config.php b/config/inc_projects_config.php index 0345ab7affa26af284cbbff71d9e6cc83aca0136..17cfa94e3e59fee88a94805e1f903765c5ff42ef 100644 --- a/config/inc_projects_config.php +++ b/config/inc_projects_config.php @@ -1,4 +1,10 @@ <?php +/** + * Array of global configuration for CI server + * @var array + */ +$aConfig = []; +global $aConfig; $aConfig = include('config_defaults.php'); if (file_exists(__DIR__.'/config_custom.php')){ diff --git a/config/lang/de-de.json b/config/lang/de-de.json index e22cebcd08e8e444b7e55313a1445b485b531360..87765b902bc095e6d38e3c44d3c93987e6220407 100644 --- a/config/lang/de-de.json +++ b/config/lang/de-de.json @@ -27,8 +27,6 @@ "overview-hint-dblclick":"Doppelklick um das Projekt [%s] zu öffnen", "overview-filter":"Filter", "overview-filter-hint":"nur dieses Projekt anzeigen", - "overview-simpleview":"zur einfachen Ansicht", - "overview-extview":"zur erweiterten Ansicht", "overview-textsearch":"Freitext-Filter (Regex)", "overview-textsearch-hint":"gesucht wird in Projektnamen, Beschreibungen, Commit-Messages", "overview-filterprj":"Projekt-Filter", @@ -179,6 +177,8 @@ "page-build-reload-branches": "Branches neu laden", "page-build-branch-on-target": "Auf der Ziel-Phase ist Branch [%s] installiert.", "page-build-switch-to-target": "Setze [%s]", + "page-build-warning-version-exists": "Im der Queue für Phase [%s] ist die Version %s bereits vorhanden!", + "page-build-warning-version-is-installed": "Im der Phase [%s] ist die Version %s bereits vorhanden!", "page-cleanup-info-archives-deleted": "Folgende Verzeichnisse wurden gelöscht", "page-cleanup-info-archives-left": "Folgende Versionen sind derzeit vorhanden - mit Angabe, wo sie verwendet werden", diff --git a/config/lang/en-en.json b/config/lang/en-en.json index 3c86ce2727e0ffb820c13447544b1176a806c93a..a909472aa54a3fcdbcb4a2adf107d6975c4cb14c 100644 --- a/config/lang/en-en.json +++ b/config/lang/en-en.json @@ -26,8 +26,6 @@ "overview-hint-dblclick":"Double click to open the project [%s]", "overview-filter":"Filter", "overview-filter-hint":"Show only this project", - "overview-simpleview":"Show simple view", - "overview-extview":"Show extended view", "overview-textsearch":"Text filter (regex)", "overview-textsearch-hint":"searches in project names, descripions, commit messages", "overview-filterprj":"Project filter", @@ -180,6 +178,8 @@ "page-build-branch-on-target": "On target phase the branch [%s] was installed.", "page-build-switch-to-target": "Set [%s]", "page-build-reload-branches": "Reload all branches", + "page-build-warning-version-exists": "In the queue of phase [%s] the version %s already exists!", + "page-build-warning-version-is-installed": "In the phase [%s] the version %s already exists!", "page-cleanup-info-archives-deleted": "The following directories have been deleted", "page-cleanup-info-archives-left": "This versions currently exist - with information of their usage", diff --git a/docker/.env b/docker/.env index edab665ef52ae19c007f68f55b3744c7be96f2bf..b7d2a5e3ac966f2c7f52f1d13a621c3db9fc4907 100644 --- a/docker/.env +++ b/docker/.env @@ -1,6 +1,6 @@ # ====================================================================== # -# GENERATED BY init.sh - template: ./templates/dot_env - e2cde05722688ff85d3a93e9cd55787e +# GENERATED BY init.sh - template: templates/dot_env - e2cde05722688ff85d3a93e9cd55787e # values to be used in docker-composer.yml # # ====================================================================== diff --git a/docker/containers/web-server/Dockerfile b/docker/containers/web-server/Dockerfile index 5faaf3faacf9e286ff7fdf89b8a9bdf2d8870817..348f987b86cf6ba56ce4fbf20ce9bfff38f99e34 100644 --- a/docker/containers/web-server/Dockerfile +++ b/docker/containers/web-server/Dockerfile @@ -1,5 +1,5 @@ # -# GENERATED BY init.sh - template: ./templates/web-server-Dockerfile - 42dce773c83597a7d05af398bdd66d15 +# GENERATED BY init.sh - template: templates/web-server-Dockerfile - 42dce773c83597a7d05af398bdd66d15 # FROM php:8.3-apache diff --git a/docker/containers/web-server/apache/sites-enabled/vhost_app.conf b/docker/containers/web-server/apache/sites-enabled/vhost_app.conf index 872493645c22d783c78e8ae433ce97580abacc5d..776ee05b453a85c32d98c8f3f9461173645e3b4a 100644 --- a/docker/containers/web-server/apache/sites-enabled/vhost_app.conf +++ b/docker/containers/web-server/apache/sites-enabled/vhost_app.conf @@ -1,5 +1,5 @@ # -# GENERATED BY init.sh - template: ./templates/vhost_app.conf - 9a9cf79de5a3584c0cef6cb79c339c25 +# GENERATED BY init.sh - template: templates/vhost_app.conf - 9a9cf79de5a3584c0cef6cb79c339c25 # diff --git a/docker/containers/web-server/php/extra-php-config.ini b/docker/containers/web-server/php/extra-php-config.ini index 5cc3af6d4826463f008b05762f398fc227f58b64..472676db8a976999da1b1239095909552595d35f 100644 --- a/docker/containers/web-server/php/extra-php-config.ini +++ b/docker/containers/web-server/php/extra-php-config.ini @@ -1,5 +1,5 @@ ; -; GENERATED BY init.sh - template: ./templates/extra-php-config.ini - 80c23edaf568e2c36b9926fe2339e481 +; GENERATED BY init.sh - template: templates/extra-php-config.ini - 80c23edaf568e2c36b9926fe2339e481 ; [PHP] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2b7f14c6947f0c4046c782abeee668332f8462bc..83c06ec44994fdb672b54f3a3fba230a64dbfeb5 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,5 +1,5 @@ # -# GENERATED BY init.sh - template: ./templates/docker-compose.yml - d3f1d9971b8c651cfcec647a3de69105 +# GENERATED BY init.sh - template: templates/docker-compose.yml - d3f1d9971b8c651cfcec647a3de69105 # # ====================================================================== # diff --git a/docker/init.sh b/docker/init.sh index 4f9d33eb3428ee7cc75e9530f6e11371af6e3449..9c7c858cb27599c8778a9028e54ca5282cd90d0f 100755 --- a/docker/init.sh +++ b/docker/init.sh @@ -20,6 +20,9 @@ # 2024-07-22 v1.13 <axel.hahn@unibe.ch> show info if there is no database container; speedup replacements # 2024-07-22 v1.14 <axel.hahn@unibe.ch> show colored boxes with container status # 2024-07-24 v1.15 <axel.hahn@unibe.ch> update menu output +# 2024-07-26 v1.16 <axel.hahn@unibe.ch> hide unnecessary menu items (WIP) +# 2024-07-29 v1.17 <www.axel-hahn.de> hide unnecessary menu items; reorder functions +# 2024-08-14 v1.18 <www.axel-hahn.de> update container view # ====================================================================== cd "$( dirname "$0" )" || exit 1 @@ -33,7 +36,7 @@ _self=$( basename "$0" ) # shellcheck source=/dev/null . "${_self}.cfg" || exit 1 -_version="1.15" +_version="1.18" # git@git-repo.iml.unibe.ch:iml-open-source/docker-php-starterkit.git selfgitrepo="docker-php-starterkit.git" @@ -43,13 +46,65 @@ fgRed="\e[31m" fgGreen="\e[32m" fgBrown="\e[33m" fgBlue="\e[34m" + +fgInvert="\e[7m" fgReset="\e[0m" +# ----- status varsiables +# running containers +DC_WEB_UP=0 +DC_DB_UP=0 + +# repo of docker-php-starterkit is here? +DC_REPO=1 + +DC_CONFIG_CHANGED=0 + +DC_WEB_URL="" # ---------------------------------------------------------------------- # FUNCTIONS # ---------------------------------------------------------------------- +# ---------------------------------------------------------------------- +# STATUS FUNCTIONS + +# get container status and set global variable DC_REPO +# DC_REPO = 0 nothing to do - repo was changed to project +# DC_REPO = 1 if repo is in selfgitrepo (must be deleted) +function _getStatus_repo(){ + DC_REPO=0 + git config --get remote.origin.url 2>/dev/null | grep -q $selfgitrepo && DC_REPO=1 +} + +# check if any of the templates has a change that must be applied +function _getStatus_template(){ + _generateFiles "dryrun" +} + +# get container status and set global variables +# DC_WEB_UP - web container +# DC_DB_UP - database container +# 0 = down +# 1 = up +function _getStatus_docker(){ + local _out + _out=$( docker-compose -p "$APP_NAME" ps) + + DC_WEB_UP=0 + DC_DB_UP=0 + grep -q "${APP_NAME}-server" <<< "$_out" && DC_WEB_UP=1 + grep -q "${APP_NAME}-db" <<< "$_out" && DC_DB_UP=1 +} + +function _getWebUrl(){ + DC_WEB_URL="$frontendurl" + grep -q "${APP_NAME}-server" /etc/hosts && DC_WEB_URL="https://${APP_NAME}-server/" +} + +# ---------------------------------------------------------------------- +# OUTPUT + # draw a headline 2 function h2(){ echo @@ -62,44 +117,70 @@ function h3(){ echo -e "$fgBlue----- $*$fgReset" } -# show help for param -h +# helper for menu: print an inverted key +function _key(){ + echo -en "\e[4;7m ${1} \e[0m" +} + +# show menu in interactive mode and list keys in help with param -h +# param string optional: set to "all" to show all menu items function showMenu(){ - cat <<EOM - $( _key g ) - remove git data of starterkit - - $( _key i ) - init application: set permissions - $( _key t ) - generate files from templates - $( _key T ) - remove generated files - - $( _key u ) - startup containers docker-compose ... up -d - $( _key U ) - startup containers docker-compose ... up -d --build - $( _key s ) - shutdown containers docker-compose stop - $( _key r ) - remove containers docker-compose rm -f - - $( _key m ) - more infos - $( _key o ) - open app [${APP_NAME}] $frontendurl - $( _key c ) - console (bash) - $( _key p ) - console check with php linter - - $( _key q ) - quit -EOM + local _bAll=0 + test -n "$1" && _bAll=1 + + local _spacer=" " + + echo + if [ $DC_REPO -eq 1 ] || [ $_bAll -eq 1 ]; then + echo "${_spacer}$( _key g ) - remove git data of starterkit" + echo + fi + echo "${_spacer}$( _key i ) - init application: set permissions" + + if [ $DC_CONFIG_CHANGED -eq 1 ] || [ $_bAll -eq 1 ]; then + echo "${_spacer}$( _key t ) - generate files from templates" + fi + if [ $DC_CONFIG_CHANGED -eq 0 ] || [ $_bAll -eq 1 ]; then + echo "${_spacer}$( _key T ) - remove generated files" + fi + echo + if [ $DC_WEB_UP -eq 0 ] || [ $_bAll -eq 1 ]; then + if [ $DC_CONFIG_CHANGED -eq 0 ] || [ $_bAll -eq 1 ]; then + echo "${_spacer}$( _key u ) - startup containers docker-compose ... up -d" + echo "${_spacer}$( _key U ) - startup containers docker-compose ... up -d --build" + echo + echo "${_spacer}$( _key r ) - remove containers docker-compose rm -f" + fi + fi + if [ $DC_WEB_UP -eq 1 ] || [ $_bAll -eq 1 ]; then + echo "${_spacer}$( _key s ) - shutdown containers docker-compose stop" + echo + echo "${_spacer}$( _key m ) - more infos" + echo "${_spacer}$( _key o ) - open app [${APP_NAME}] $DC_WEB_URL" + echo "${_spacer}$( _key c ) - console (bash)" + echo "${_spacer}$( _key p ) - console check with php linter" + fi + echo + echo "${_spacer}$( _key q ) - quit" + } function showHelp(){ cat <<EOH + INITIALIZER FOR DOCKER APP v$_version A helper script written in Bash to bring up a PHP+Mysql application in docker. -Source : https://git-repo.iml.unibe.ch/iml-open-source/docker-php-starterkit -Docs : https://os-docs.iml.unibe.ch/docker-php-starterkit/ -License: GNU GPL 3.0 +📄 Source : https://git-repo.iml.unibe.ch/iml-open-source/docker-php-starterkit +📗 Docs : https://os-docs.iml.unibe.ch/docker-php-starterkit/ +📜 License: GNU GPL 3.0 (c) Institute for Medical Education; University of Bern SYNTAX: $_self [-h|-v] - $_self [menu key] + $_self [menu key [.. menu key N]] OPTIONS: -h show this help and exit @@ -110,7 +191,7 @@ MENU KEYS: The same keys can be put as parameter to start this action. You can add multiples keys to apply multiple actions. -$( showMenu ) +$( showMenu "all" ) EXAMPLES: @@ -122,6 +203,56 @@ EXAMPLES: EOH } + +# show urls for app container +function _showBrowserurl(){ + echo "In a web browser open:" + echo " $DC_WEB_URL" +} + +# detect + show ports and urls for app container and db container +function _showInfos(){ + _showContainers long + h2 INFO + + h3 "processes webserver" + # docker-compose top + docker top "${APP_NAME}-server" + if [ ! "$DB_ADD" = "false" ]; then + h3 "processes database" + docker top "${APP_NAME}-db" + fi + + h3 "What to open in browser" + if echo >"/dev/tcp/localhost/${APP_PORT}"; then + # echo "OK, app port ${APP_PORT} is reachable" + # echo + _showBrowserurl + else + echo "ERROR: app port ${APP_PORT} is not available" + fi 2>/dev/null + + if [ "$DB_ADD" != "false" ]; then + h3 "Check database port" + if echo >"/dev/tcp/localhost/${DB_PORT}"; then + echo "OK, db port ${DB_PORT} is reachable" + echo + echo "In a local DB admin tool you can connect it:" + echo " host : localhost" + echo " port : ${DB_PORT}" + echo " user : root" + echo " password: ${MYSQL_ROOT_PASS}" + else + echo "NO, db port ${DB_PORT} is not available" + fi 2>/dev/null + + fi + echo +} + +# ---------------------------------------------------------------------- +# ACTIONS + # set acl on local directory function _setWritepermissions(){ h2 "set write permissions on ${gittarget} ..." @@ -156,7 +287,7 @@ function _removeGitdata(){ h2 "Remove git data of starterkit" echo -n "Current git remote url: " git config --get remote.origin.url - if git config --get remote.origin.url 2>/dev/null | grep $selfgitrepo >/dev/null; then + if git config --get remote.origin.url 2>/dev/null | grep -q $selfgitrepo; then echo echo -n "Delete local .git and .gitignore? [y/N] > " read -r answer @@ -180,7 +311,7 @@ function _fix_no-db(){ fi } -# helper functiion to generate replacements using sed +# helper function to generate replacements using sed # it loops over all vars in the config file # used in _generateFiles function _getreplaces(){ @@ -202,15 +333,21 @@ function _getreplaces(){ # It skips if # - 1st line is not starting with "# TARGET: filename" # - target file has no updated lines +# If the 1st parameter is set to "dryrun" it will not generate files. +# param string dryrun optional: set to "dryrun" to not generate files function _generateFiles(){ + local _dryrun="$1" + DC_CONFIG_CHANGED=0 + # shellcheck source=/dev/null . "${_self}.cfg" || exit 1 params=$( _getreplaces | while read -r line; do echo -n "-e '$line' "; done ) local _tmpfile=/tmp/newfilecontent$$.tmp - h2 "generate files from templates..." + + test "$_dryrun" = "dryrun" || h2 "generate files from templates..." for mytpl in templates/* do # h3 $mytpl @@ -220,7 +357,9 @@ function _generateFiles(){ target=$( head -1 "$mytpl" | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' ) if [ -z "$target" ]; then - echo "SKIP: $mytpl - target was not found in 1st line" + if [ "$_dryrun" != "dryrun" ]; then + echo "SKIP: $mytpl - target was not found in 1st line" + fi _doReplace=0 fi @@ -235,20 +374,28 @@ function _generateFiles(){ local _md5; _md5=$( md5sum $_tmpfile | awk '{ print $1 }' ) sed -i "s#{{generator}}#GENERATED BY $_self - template: $mytpl - $_md5#g" $_tmpfile + # apply all replacements to the tmp file eval sed -i "$params" "$_tmpfile" || exit _fix_no-db $_tmpfile # echo "changes for $target:" - if diff --color=always "../$target" "$_tmpfile" | grep -v "$_md5" | grep -v "^---" | grep . || [ ! -f "../$target" ]; then - echo -n "$mytpl - changes detected - writing [$target] ... " - mkdir -p "$( dirname ../"$target" )" || exit 2 - mv "$_tmpfile" "../$target" || exit 2 - echo OK - echo + if diff --color=always "../$target" "$_tmpfile" 2>/dev/null | grep -v "$_md5" | grep -v "^---" | grep . || [ ! -f "../$target" ]; then + if [ "$_dryrun" = "dryrun" ] + then + DC_CONFIG_CHANGED=1 + else + echo -n "$mytpl - changes detected - writing [$target] ... " + mkdir -p "$( dirname ../"$target" )" || exit 2 + mv "$_tmpfile" "../$target" || exit 2 + echo OK + echo + fi else rm -f $_tmpfile - echo "SKIP: $mytpl - Nothing to do." + if [ "$_dryrun" != "dryrun" ]; then + echo "SKIP: $mytpl - Nothing to do." + fi fi fi done @@ -278,9 +425,11 @@ function _removeGeneratedFiles(){ done } + # show running containers function _showContainers(){ local bLong=$1 + local _out local sUp=".. UP" @@ -288,56 +437,42 @@ function _showContainers(){ local Status= local StatusWeb="$sDown" - local StatusDb="" + local StatusDb="$sDown" local colWeb= local colDb= colDb="$fgRed" colWeb="$fgRed" - _out=$( if [ -z "$bLong" ]; then - docker-compose -p "$APP_NAME" ps - else - # docker ps | grep "$APP_NAME" - docker-compose -p "$APP_NAME" ps - fi) + if [ $DC_WEB_UP -eq 1 ]; then + colWeb="$fgGreen" + StatusWeb="$sUp" + fi + + if [ $DC_DB_UP -eq 1 ]; then + colDb="$fgGreen" + StatusDb="$sUp" + fi - h2 CONTAINERS - if [ "$( wc -l <<< "$_out" )" -eq 1 ]; then - if [ "$DB_ADD" = "false" ]; then - colDb="$fgGray" - Status="The web container is not running. This app has no database container." - else - StatusDb="down" - Status="No container is running for <$APP_NAME>." - fi - else - grep -q "${APP_NAME}-server" <<< "$_out" && colWeb="$fgGreen" - grep -q "${APP_NAME}-server" <<< "$_out" && StatusWeb="$sUp" + if [ "$DB_ADD" = "false" ]; then + colDb="$fgGray" + local StatusDb=".. N/A" + Status="This app has no database container." + fi - grep -q "${APP_NAME}-db" <<< "$_out" && colDb="$fgGreen" - StatusDb="$sDown" - grep -q "${APP_NAME}-db" <<< "$_out" && StatusDb="$sUp" + h2 CONTAINERS - if [ "$DB_ADD" = "false" ]; then - colDb="$fgGray" - StatusDb="" - Status="INFO: This app has no database container." - fi - fi + echo + printf " $colWeb$fgInvert %-32s $fgReset $colDb$fgInvert %-32s $fgReset\n" "WEB ${StatusWeb}" "DB ${StatusDb}" + printf " %-32s $fgReset %-32s $fgReset\n" "PHP ${APP_PHP_VERSION}" "${MYSQL_IMAGE}" + printf " %-32s $fgReset %-32s $fgReset\n" ":${APP_PORT}" ":${DB_PORT}" - printf "$colWeb __________________________ $colDb __________________________ $fgReset \n" - printf "$colWeb | %-22s | $colDb | %-22s | $fgReset \n" "" "" - printf "$colWeb | %-22s | $colDb | %-22s | $fgReset \n" "${APP_NAME}-web ${StatusWeb}" "${APP_NAME}-db ${StatusDb}" - printf "$colWeb | %-22s | $colDb | %-22s | $fgReset \n" " PHP ${APP_PHP_VERSION}" " ${MYSQL_IMAGE}" - printf "$colWeb | %-22s | $colDb | %-22s | $fgReset \n" " :${APP_PORT}" " :${DB_PORT}" - printf "$colWeb |__________________________| $colDb |__________________________| $fgReset \n" + echo if [ -n "$Status" ]; then + echo " $Status" echo - echo "$Status" fi - echo if [ -n "$bLong" ]; then echo "$_out" @@ -349,61 +484,6 @@ function _showContainers(){ } - -# show urls for app container -function _showBrowserurl(){ - echo "In a web browser open:" - echo " $frontendurl" - if grep "${APP_NAME}-server" /etc/hosts >/dev/null; then - echo " https://${APP_NAME}-server/" - fi -} - -# detect + show ports and urls for app container and db container -function _showInfos(){ - _showContainers long - h2 INFO - - h3 "processes webserver" - # docker-compose top - docker top "${APP_NAME}-server" - if [ ! "$DB_ADD" = "false" ]; then - h3 "processes database" - docker top "${APP_NAME}-db" - fi - - h3 "Check app port" - if echo >"/dev/tcp/localhost/${APP_PORT}"; then - echo "OK, app port ${APP_PORT} is reachable" - echo - _showBrowserurl - else - echo "NO, app port ${APP_PORT} is not available" - fi 2>/dev/null - - if [ "$DB_ADD" != "false" ]; then - h3 "Check database port" - if echo >"/dev/tcp/localhost/${DB_PORT}"; then - echo "OK, db port ${DB_PORT} is reachable" - echo - echo "In a local DB admin tool you can connect it:" - echo " host : localhost" - echo " port : ${DB_PORT}" - echo " user : root" - echo " password: ${MYSQL_ROOT_PASS}" - else - echo "NO, db port ${DB_PORT} is not available" - fi 2>/dev/null - - fi - echo -} - -# helper for menu: print an inverted key -function _key(){ - echo -en "\e[4;7m ${1} \e[0m" -} - # helper: wait for a return key function _wait(){ local _wait=15 @@ -418,13 +498,16 @@ action=$1; shift 1 while true; do + _getStatus_repo + _getStatus_docker + _getStatus_template + _getWebUrl + if [ -z "$action" ]; then echo "_______________________________________________________________________________" echo - echo - echo " ${APP_NAME^^} :: Initializer for docker" - echo " ______" + printf " %-70s ______\n" "${APP_NAME^^} :: Initializer for docker" echo "________________________________________________________________________/ $_version" echo @@ -524,7 +607,7 @@ while true; do ;; o) h2 "Open app ..." - xdg-open "$frontendurl" + xdg-open "$DC_WEB_URL" ;; q) h2 "Bye!" diff --git a/docs/10_Get_started/20_Installation_with_Docker.md b/docs/10_Get_started/20_Installation_with_Docker.md index 8ced80c07fd13ba9b91c0f7db4c77a7782800c88..348f0a147bf8024dd9ff30b0b10d21ce0a4b992f 100644 --- a/docs/10_Get_started/20_Installation_with_Docker.md +++ b/docs/10_Get_started/20_Installation_with_Docker.md @@ -1,6 +1,7 @@ # Installation with a local Docker service For development a docker environment is part of the repository data. +You can use it for a first view on your own machine before installing it. ## Requirements diff --git a/docs/10_Get_started/30_Filestructure.md b/docs/10_Get_started/30_Filestructure.md index 756107f978c7e9f8c5a50c8048e9a39939c9baf3..2274777a15e3ca7e5e614d6c3f61acccf1594ca2 100644 --- a/docs/10_Get_started/30_Filestructure.md +++ b/docs/10_Get_started/30_Filestructure.md @@ -53,9 +53,6 @@ Default: /var/www/[YOUR-DOMAIN]/public_html/ │ └── 4.21.0 │ └── img │ └── network -├── versions -│ ├── classes -│ └── data └── webservice ``` diff --git a/hooks/templates/config_custom.php.erb b/hooks/templates/config_custom.php.erb index 01e109e323cf18997723d7657af460f1907b7dc9..e128b89b9492e10c972785da2b79bc7824072458 100644 --- a/hooks/templates/config_custom.php.erb +++ b/hooks/templates/config_custom.php.erb @@ -103,15 +103,37 @@ return [ ], /* - 'foreman' => array( + 'foreman' => [ 'api'=>'<%= @replace["foreman-url"] %>', // with ending "/" 'user'=>'<%= @replace["foreman-user"] %>', 'password'=>'<%= @replace["foreman-password"] %>', 'ignore-ssl-error'=><%= @replace["foreman-ignore-ssl-error"] %>, // 'varname-replace'=>'ci-replacement', - ), + ], */ // ---------------------------------------------------------------------- + // APPMONITOR + // see <https://os-docs.iml.unibe.ch/appmonitor/> + // ---------------------------------------------------------------------- + + "appmonitor" => [ + + // notification + + // email notification for this application + "email" => [<%= @replace["appmonitor-email-to"] %>], + + "slack" => [<%= @replace["appmonitor-slack"] %>], + + // limit access to appmonitor client to specific IP addresses + "ip" => [<%= @replace["appmonitor-ip"] %>], + + // limit access to appmonitor client to specific token + "token" => [<%= @replace["appmonitor-token"] %>], + + ], + + // ---------------------------------------------------------------------- ]; \ No newline at end of file diff --git a/public_html/api/index.php b/public_html/api/index.php index 31063b5ec3020186b873c77161bc7b24198e886a..15c0b25ad5c8cb4889cc86851f94ec17d18d976b 100644 --- a/public_html/api/index.php +++ b/public_html/api/index.php @@ -11,245 +11,271 @@ * ---------------------------------------------------------------------- * 2020-06-16 v0.9 <axel.hahn@iml.unibe.ch> * 2021-03-29 v1.2 <axel.hahn@iml.unibe.ch> support slashes in branch names + * 2024-09-02 v1.3 <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax * ====================================================================== */ - $bDebug=false; - ini_set('display_errors', 1); - ini_set('display_startup_errors', 1); - error_reporting(E_ALL); - - require_once("../../config/inc_projects_config.php"); - - $sDirClasses=__DIR__.'/../deployment/classes/'; - require_once($sDirClasses.'/project.class.php'); - require_once($sDirClasses.'logger.class.php'); - - $iMaxAge=60; - - // ---------------------------------------------------------------------- - // FUNCTIONS - // ---------------------------------------------------------------------- - /** - * write debug text (if enabled) - * @global boolean $bDebug - * @param string $s message - * @param string $sLevel level; one of info| - * @return boolean - */ - function _wd($s, $sLevel='info'){ - global $bDebug; - if ($bDebug){ - echo '<div class="debug debug-'.$sLevel.'">DEBUG: '.$s.'</div>'; - } - return true; +$bDebug = false; +ini_set('display_errors', 1); +ini_set('display_startup_errors', 1); +error_reporting(E_ALL); + +/** + * Path to deployment classes + * @var string + */ + $sDirClasses = __DIR__ . '/../deployment/classes/'; + + /** + * Allowed time delta for client or server + * @var integer + */ +$iMaxAge = 60; + +require_once("../../config/inc_projects_config.php"); + +require_once($sDirClasses . '/project.class.php'); +require_once($sDirClasses . 'logger.class.php'); + +// ---------------------------------------------------------------------- +// FUNCTIONS +// ---------------------------------------------------------------------- +/** + * Write debug text (if enabled) + * + * @global boolean $bDebug + * + * @param string $s message + * @param string $sLevel level; one of info| + * @return boolean + */ +function _wd(string $s, string $sLevel = 'info'): bool +{ + global $bDebug; + if ($bDebug) { + echo "<div class=\"debug debug-$sLevel\">DEBUG: $s</div>"; } - - /** - * abort execution with error - * @param string $s message - * @param integer $iStatus http status code to send - */ - function _quit($s, $iStatus=400){ - $aStatus=array( - 400=>'HTTP/1.0 400 Bad Request', - 403=>'HTTP/1.0 403 Access denied', - 404=>'HTTP/1.0 404 Not found', - ); - header($aStatus[$iStatus]); - _done(array('status'=>$iStatus, 'info'=>$aStatus[$iStatus], 'message'=>$s)); + return true; +} + +/** + * Abort execution of API requestwith error + * + * @param string $s message + * @param integer $iStatus http status code to send + */ +function _quit(string $s, int $iStatus = 400): void +{ + $aStatus = [ + 400 => 'HTTP/1.0 400 Bad Request', + 403 => 'HTTP/1.0 403 Access denied', + 404 => 'HTTP/1.0 404 Not found', + ]; + header($aStatus[$iStatus]); + _done(['status' => $iStatus, 'info' => $aStatus[$iStatus], 'message' => $s]); +} + +/** + * End with OK output + * + * @param array $Data array data to show as JSON + * @return void + */ +function _done(array $Data): void +{ + echo is_array($Data) + ? json_encode($Data, JSON_PRETTY_PRINT) + : $Data + ; + die(); +} + +/** + * Check authorization in the http request header and age of timestamp + * On a failed check the request will be terminated + * + * @global int $iMaxAge max allowed age + * + * @param string $sProjectSecret + * @return boolean + */ +function _checkAuth(string $sProjectSecret): bool +{ + global $iMaxAge; + $aReqHeaders = apache_request_headers(); + _wd('<pre>' . print_r($aReqHeaders, 1) . '</pre>'); + if (!isset($aReqHeaders['Authorization'])) { + _quit('Access denied. Missing authorization.', 403); } - /** - * end with OK output - * @param type $Data - */ - function _done($Data){ - echo is_array($Data) - ? json_encode($Data, JSON_PRETTY_PRINT) - : $Data - ; - die(); + if (!isset($aReqHeaders['Date'])) { + _quit('Access denied. Missing field "Date:" in the request header.', 403); } - - /** - * Check authorization in the http request header and age of timestamp - * On a failed check the request will be terminated - * @global int $iMaxAge max allowed age - * @param string $sProjectSecret - * @return boolean - */ - function _checkAuth($sProjectSecret){ - global $iMaxAge; - $aReqHeaders=apache_request_headers(); - _wd('<pre>'.print_r($aReqHeaders, 1).'</pre>'); - if(!isset($aReqHeaders['Authorization'])){ - _quit('Access denied. Missing authorization.', 403); - } - if(!isset($aReqHeaders['Date'])){ - _quit('Access denied. Missing field "Date:" in the request header.', 403); - } - - $sGotHash= preg_replace('/^.*\:/', '', $aReqHeaders['Authorization']); - $sGotDate= $aReqHeaders['Date']; - $sGotMethod=$_SERVER['REQUEST_METHOD']; - $sGotReq=$_SERVER['REQUEST_URI']; - - - $sMyData="$sGotMethod\n$sGotReq\n$sGotDate\n"; - $sMyHash= base64_encode(hash_hmac("sha1", $sMyData, $sProjectSecret)); - - _wd('Hash: '.$sGotHash.' -- from header'); - _wd('Hash: '.$sMyHash.' -- rebuilt'); - if($sGotHash!==$sMyHash){ - _quit('Access denied. Invalid hash.', 403); - } - $iAge=date('U')-date('U', strtotime($sGotDate)); - _wd('Date: '.$sGotDate.' - age: '.$iAge.' sec'); - if($iAge>$iMaxAge){ - _quit('Access denied. Hash is out of date: '.$iAge. ' sec is older '.$iMaxAge.' sec. Maybe client or server is out of sync.', 403); - } - if($iAge<-$iMaxAge){ - _quit('Access denied. Hash is '.$iAge. ' sec in future but only '.$iMaxAge.' sec are allowed. Maybe client or server is out of sync.', 403); - } - return true; + $sGotHash = preg_replace('/^.*\:/', '', $aReqHeaders['Authorization']); + $sGotDate = $aReqHeaders['Date']; + $sGotMethod = $_SERVER['REQUEST_METHOD']; + $sGotReq = $_SERVER['REQUEST_URI']; + + + $sMyData = "$sGotMethod\n$sGotReq\n$sGotDate\n"; + $sMyHash = base64_encode(hash_hmac("sha1", $sMyData, $sProjectSecret)); + + _wd('Hash: ' . $sGotHash . ' -- from header'); + _wd('Hash: ' . $sMyHash . ' -- rebuilt'); + if ($sGotHash !== $sMyHash) { + _quit('Access denied. Invalid hash.', 403); + } + + $iAge = date('U') - date('U', strtotime($sGotDate)); + _wd('Date: ' . $sGotDate . ' - age: ' . $iAge . ' sec'); + if ($iAge > $iMaxAge) { + _quit('Access denied. Hash is out of date: ' . $iAge . ' sec is older ' . $iMaxAge . ' sec. Maybe client or server is out of sync.', 403); } - // ---------------------------------------------------------------------- - // MAIN - // ---------------------------------------------------------------------- - if (!$bDebug){ - header('Content-Type: application/json'); + if ($iAge < -$iMaxAge) { + _quit('Access denied. Hash is ' . $iAge . ' sec in future but only ' . $iMaxAge . ' sec are allowed. Maybe client or server is out of sync.', 403); } - _wd('Start: '.date('Y-m-d H:i:s').'<style>body{background:#eee; color:#456;} + return true; +} +// ---------------------------------------------------------------------- +// MAIN +// ---------------------------------------------------------------------- +if (!$bDebug) { + header('Content-Type: application/json'); +} +_wd('Start: ' . date('Y-m-d H:i:s') . '<style>body{background:#eee; color:#456;} .debug{background:#ddd; margin-bottom: 2px;} </style>'); - _wd('request uri is '.$_SERVER["REQUEST_URI"]); - _wd('<pre>GET: '.print_r($_GET, 1).'</pre>'); - - - // ---------- SPLIT URL - - $aUriSplit= explode('/', preg_replace('/\?.*$/', '', $_SERVER["REQUEST_URI"])); - - array_shift($aUriSplit); - array_shift($aUriSplit); - _wd('<pre>$aUriSplit: '.print_r($aUriSplit, 1).'</pre>'); - /* - - /api/v1/projects/ci/build/... - $aUriSplit: Array - ( - [0] => v1 - [1] => projects - [2] => ci - [3] => build - ) - */ - $sApiVersion = isset($aUriSplit[0]) ? $aUriSplit[0] : false; - $sApiItem = isset($aUriSplit[1]) ? $aUriSplit[1] : false; - - - if(!$sApiVersion){ - _quit('ERROR: no param for api version was found.'); - } - if(!$sApiItem){ - _quit('ERROR: no param for item was found.'); - } - - - switch ($sApiVersion){ - case 'v1': - switch($sApiItem){ - case 'projects': - - $oProject=new project(); - $aList=$oProject->getProjects(); - _wd('<pre>'.print_r($aList,1).'</pre>'); - _done($aList); - break;; - - case 'project': - // path /api/v1/project - - $sPrjId = isset($aUriSplit[2]) ? $aUriSplit[2] : false; - $sPrjAction = isset($aUriSplit[3]) ? $aUriSplit[3] : false; - $sBranch = implode('/', array_slice($aUriSplit, 4)); - - // $sParam4 = isset($aUriSplit[4]) ? $aUriSplit[4] : false; - // $sParam5 = isset($aUriSplit[5]) ? $aUriSplit[5] : false; - $sMethod = $_SERVER['REQUEST_METHOD']; - _wd('$sPrjId = '.$sPrjId); - _wd('$sPrjAction = '.$sPrjAction); - _wd('$sBranch = ' . $sBranch); - - $oCLog = new logger(); - // try to init the given project - try{ - ob_start(); - $oProject=new project($sPrjId); - - // $oProject->setProjectById($sPrjId); - ob_end_clean(); - - } catch (Exception $exc) { - _quit('ERROR: project with id ['.$sPrjId.'] does not exist.', 404); - } - - // get secret - $aPrjCfg=$oProject->getConfig(); - $sProjectSecret=isset($aPrjCfg['api']['secret']) ? $aPrjCfg['api']['secret'] : false; - if(!$sProjectSecret){ - _quit('Access denied. API access is disabled.'); - } - - // check authorization - _checkAuth($sProjectSecret); - - // echo "OK: request was authorized successfully.\n"; - - $oProject->oUser->setUser('api'); - - switch($sPrjAction){ - case "build": - if ($sBranch){ - $aResult=$oProject->setBranchname($sBranch); - } - $sBranchname=$oProject->getBranchname(); - $aRepodata = $oProject->getRemoteBranches(true); // ignore cache = true - if(!isset($aRepodata[$sBranchname])){ - _quit('ERROR: branch not found: '.$sBranchname, 404); - } - - - // echo "branch is set to ".$oProject->getBranchname()."\n"; - if ($sMethod==='GET'){ - $sNext=$oProject->getNextPhase(); - _done(array( - 'branch'=>$sBranchname, - 'phase'=>$sNext, - 'repo'=>$aRepodata[$sBranchname] - )); - } - if ($sMethod==='POST'){ - echo "starting build() ..."; - flush(); - echo $oProject->build(); - } - break;; - case "phases": - _done($oProject->getAllPhaseInfos()); - break;; - default: - _quit('ERROR: Wrong action ['.$sApiItem.'].'); - } - - break;; - - default: - _quit('ERROR: item ['.$sApiItem.'] is invalid.'); - } - break; - default: - _quit('ERROR: Wrong (unsupported) api version.'); - } \ No newline at end of file +_wd('request uri is ' . $_SERVER["REQUEST_URI"]); +_wd('<pre>GET: ' . print_r($_GET, 1) . '</pre>'); + + +// ---------- SPLIT URL + +$aUriSplit = explode('/', preg_replace('/\?.*$/', '', $_SERVER["REQUEST_URI"])); + +array_shift($aUriSplit); +array_shift($aUriSplit); +_wd('<pre>$aUriSplit: ' . print_r($aUriSplit, 1) . '</pre>'); +/* + +/api/v1/projects/ci/build/... +$aUriSplit: Array + ( + [0] => v1 + [1] => projects + [2] => ci + [3] => build + ) + */ +$sApiVersion = isset($aUriSplit[0]) ? $aUriSplit[0] : false; +$sApiItem = isset($aUriSplit[1]) ? $aUriSplit[1] : false; + + +if (!$sApiVersion) { + _quit('ERROR: no param for api version was found.'); +} +if (!$sApiItem) { + _quit('ERROR: no param for item was found.'); +} + + +switch ($sApiVersion) { + case 'v1': + switch ($sApiItem) { + case 'projects': + + $oProject = new project(); + $aList = $oProject->getProjects(); + _wd('<pre>' . print_r($aList, 1) . '</pre>'); + _done($aList); + break; + ; + + case 'project': + // path /api/v1/project + + $sPrjId = isset($aUriSplit[2]) ? $aUriSplit[2] : false; + $sPrjAction = isset($aUriSplit[3]) ? $aUriSplit[3] : false; + $sBranch = implode('/', array_slice($aUriSplit, 4)); + + // $sParam4 = isset($aUriSplit[4]) ? $aUriSplit[4] : false; + // $sParam5 = isset($aUriSplit[5]) ? $aUriSplit[5] : false; + $sMethod = $_SERVER['REQUEST_METHOD']; + _wd('$sPrjId = ' . $sPrjId); + _wd('$sPrjAction = ' . $sPrjAction); + _wd('$sBranch = ' . $sBranch); + + $oCLog = new logger(); + // try to init the given project + try { + ob_start(); + $oProject = new project($sPrjId); + + // $oProject->setProjectById($sPrjId); + ob_end_clean(); + + } catch (Exception $exc) { + _quit('ERROR: project with id [' . $sPrjId . '] does not exist.', 404); + } + + // get secret + $aPrjCfg = $oProject->getConfig(); + $sProjectSecret = isset($aPrjCfg['api']['secret']) ? $aPrjCfg['api']['secret'] : false; + if (!$sProjectSecret) { + _quit('Access denied. API access is disabled.'); + } + + // check authorization + _checkAuth($sProjectSecret); + + // echo "OK: request was authorized successfully.\n"; + + $oProject->oUser->setUser('api'); + + switch ($sPrjAction) { + case "build": + if ($sBranch) { + $aResult = $oProject->setBranchname($sBranch); + } + $sBranchname = $oProject->getBranchname(); + $aRepodata = $oProject->getRemoteBranches(true); // ignore cache = true + if (!isset($aRepodata[$sBranchname])) { + _quit('ERROR: branch not found: ' . $sBranchname, 404); + } + + + // echo "branch is set to ".$oProject->getBranchname()."\n"; + if ($sMethod === 'GET') { + $sNext = $oProject->getNextPhase(); + _done([ + 'branch' => $sBranchname, + 'phase' => $sNext, + 'repo' => $aRepodata[$sBranchname] + ]); + } + if ($sMethod === 'POST') { + echo "starting build() ..."; + flush(); + echo $oProject->build(); + } + break; + ; + case "phases": + _done($oProject->getAllPhaseInfos()); + break; + ; + default: + _quit('ERROR: Wrong action [' . $sApiItem . '].'); + } + + break; + ; + + default: + _quit('ERROR: item [' . $sApiItem . '] is invalid.'); + } + break; + default: + _quit('ERROR: Wrong (unsupported) api version.'); +} \ No newline at end of file diff --git a/public_html/appmonitor/general_include.php b/public_html/appmonitor/general_include.php index bdd129b19018a17c8f27d750d304e9198ccf12bb..b7530759bed2059dace5d912f142a0816c344d6e 100644 --- a/public_html/appmonitor/general_include.php +++ b/public_html/appmonitor/general_include.php @@ -7,6 +7,7 @@ * @author: Axel Hahn * ---------------------------------------------------------------------- * 2018-06-30 v0.1 + * 2024-09-04 php8 only: short array syntax; use data from ci server config file */ // ---------------------------------------------------------------------- @@ -17,28 +18,31 @@ // check local ips and IML networks (includes the monitor) // appmonitor is not available on EDUROAM or VPN -$oMonitor->checkIp(array( - '127.0.0.1', - '::1', - '10.0.2.2', - '130.92.30.11', - '130.92.30.44', - '130.92.79.49', -)); +if(isset($aConfig['appmonitor']['ip']) && count($aConfig['appmonitor']['ip'])){ + $oMonitor->checkIp($aConfig['appmonitor']['ip']); +} // --- check a token // an incoming request must have the GET param "token=123" // $oMonitor->checkTokem('token', '123'); - +if(isset($aConfig['appmonitor']['token']) && count($aConfig['appmonitor']['token'])){ + $oMonitor->checkToken($aConfig['appmonitor']['token'][0], $aConfig['appmonitor']['token'][1]); +} // ---------------------------------------------------------------------- // NOTIFICATION // ---------------------------------------------------------------------- -// $oMonitor->addEmail('sysadmin@example.com'); -// $oMonitor->addSlackWebhook(array("mywebhook"=> "https://hooks.slack.com/services/(...)")); -$oMonitor->addEmail('axel.hahn@iml.unibe.ch'); - +if(isset($aConfig['appmonitor']['email']) && count($aConfig['appmonitor']['email'])){ + foreach($aConfig['appmonitor']['email'] as $sEmailTo){ + $oMonitor->addEmail($sEmailTo); + } +} +if(isset($aConfig['appmonitor']['slack']) && count($aConfig['appmonitor']['slack'])){ + foreach($aConfig['appmonitor']['slack'] as $aSlackTarget){ + $oMonitor->addSlackWebhook($aSlackTarget[0], $aSlackTarget[1]); + } +} // ---------------------------------------------------------------------- // set a tag with phase @@ -46,7 +50,9 @@ $oMonitor->addEmail('axel.hahn@iml.unibe.ch'); $sHost=$_SERVER['HTTP_HOST']; $sHost2=php_uname("n"); $sMyPhase='live'; -foreach (array('dev', 'preview', 'stage', 'demo') as $sPhase){ + +// foreach (array('dev', 'preview', 'stage', 'demo') as $sPhase){ +foreach (array_keys($aConfig['phases']) as $sPhase){ if( strstr($sHost.'.', $sPhase)!==false || strstr($sHost.'-', $sPhase)!==false diff --git a/public_html/appmonitor/git_update_appmonitor.sh b/public_html/appmonitor/git_update_appmonitor.sh index 32f5b785e927960c8c393e213c4fdb1481206d2b..117d5084dc6473c2199cb3c23dcb070ba8e6462a 100755 --- a/public_html/appmonitor/git_update_appmonitor.sh +++ b/public_html/appmonitor/git_update_appmonitor.sh @@ -1,7 +1,19 @@ #!/bin/bash # ====================================================================== # -# A P P M O N I T O R :: CLIENT - UPDATE +# +# _____ _____ __ _____ _ _ +# | | | | ___ ___ ___| |___ ___|_| |_ ___ ___ +# |- -| | | | |__ | .'| . | . | | | | . | | | _| . | _| +# |_____|_|_|_|_____| |__,| _| _|_|_|_|___|_|_|_|_| |___|_| +# |_| |_| +# _ _ _ +# ___| |_|___ ___| |_ +# | _| | | -_| | _| +# |___|_|_|___|_|_|_| +# +# +# INSTALLER + UPDATER # # This script will install or update the appmonitor client only. # @@ -16,6 +28,7 @@ # 2022-04-12 0.2 <axel.hahn@iml.unibe.ch> add help; exclude unneeded files # 2022-05-03 0.3 <axel.hahn@iml.unibe.ch> create general_include.php # 2024-07-25 0.4 <axel.hahn@iml.unibe.ch> update quoting and comments +# 2024-07-31 0.5 <axel.hahn@iml.unibe.ch> Show more helpful information; wait on 1st install; added param -n # ====================================================================== # ---------------------------------------------------------------------- @@ -23,12 +36,16 @@ # ---------------------------------------------------------------------- readonly git_repo_url="https://github.com/iml-it/appmonitor.git" +readonly docs_url="https://os-docs.iml.unibe.ch/appmonitor/PHP_client/index.html" readonly line="______________________________________________________________________________" -readonly version="0.4" +readonly version="0.5" git_target=/tmp/git_data__appmonitor client_from="${git_target}/public_html/client" client_to="." +isUpdate=0 +wait=1 + cd "$( dirname "$0" )" || exit 1 @@ -88,20 +105,22 @@ function _gitUpdate(){ # ---------------------------------------------------------------------- cat <<ENDOFHEADER +$line + + IML Appmonitor client :: installer + updater v$version +$line - +-----------------------------------+ - | | - | INSTALLER | | - | + | Appmonitor client | - | UPDATER | | - | | - +--------------------------- v$version --+ ENDOFHEADER case "$1" in -h|--help) cat <<ENDOFHELP + The IML Appmonitor is free software. + + Source: https://github.com/iml-it/appmonitor + Docs: https://os-docs.iml.unibe.ch/appmonitor + License: GNU GPL 3.0 This is a helper script to get the files of the IML Appmonitor client part only. @@ -116,18 +135,26 @@ case "$1" in On additional runs it updates the files. USAGE: + $0 [OPTIONS] [TARGET] - $0 [target path] + OPTIONS: + -h|--help + Show this help and exit + -n|--nowait + Do not wait for RETURN on 1st installation. + Use it for an unattended installation. - default target is [.] (current directory) - - $0 -h|--help - - Show this help. + PARAMETERS: + TARGET + optional target path for the client files + default target is "." (current directory) ENDOFHELP exit 0 ;; + -n|--nowait) + wait=0 + ;; *) if test -n "$1" then @@ -144,6 +171,32 @@ esac which rsync >/dev/null || exit 1 which git >/dev/null || exit 1 +test -f general_include.php && isUpdate=1 + +if [ $isUpdate -eq 0 ]; then + cat <<WELCOME + Welcome to the Appmonitor client installation! + + + This is a helper script to get the client files of the IML Appmonitor. + They will be installed into the directory "$client_to" $( test "$client_to" = "." && (echo; echo -n " "; pwd) ) + + If this is not correct, press Ctrl + C to abort and use a + parameter to set another target directory. + + "$( basename "$0" ) -h" shows a help and more options. + + +WELCOME + if [ $wait -eq 1 ]; then + echo -n " RETURN to continue ... " + read -r + fi +else + echo "Updating local files ..." +fi +echo + echo $line echo ">>> #1 of 3 >>> update local git data" echo @@ -179,7 +232,32 @@ echo diff --color -r "$client_from" "$client_to" echo +if [ $isUpdate -eq 0 ]; then + _fileupdate index.sample.php + cat <<INTRODUCTION +$line + + + DONE! + The Appmonitor client was installed. + + - Please edit index.php and general_include.php. + - If you have multiple applications below webroot then you can + rename the file index.php to check-[appname].php eg. + check-cms.php, check-blog.php, ... + + - Start "$( basename "$0" )" again to perform an update. + Maybe you want to create a cronjob for this. + +INTRODUCTION +else + echo "Appmonitor client was updated." +fi +echo + +echo "Documentation: $docs_url" +echo echo $line echo done. diff --git a/public_html/appmonitor/index.php b/public_html/appmonitor/index.php index 15011824790473dfd0a5dbd6d1d82c227ef4566f..422c047b54f93e304b1a7d104fe718326e08edc4 100644 --- a/public_html/appmonitor/index.php +++ b/public_html/appmonitor/index.php @@ -1,46 +1,69 @@ <?php - +/** + * IML APPMONITOR CHECKS FOR CI SERVER + * + * @author: Axel Hahn + * + * ------------------------------------------------------------------ + * 2014-10-24 v0.1 + * ... + * 2024-09-04 php8 only: short array syntax + */ require_once('classes/appmonitor-client.class.php'); + + require_once(__DIR__.'/../deployment/classes/project.class.php'); $oMonitor = new appmonitor(); - -$oMonitor->addCheck( - array( - "name" => "simple", - "description" => "Very simple test", - "check" => array( - "function" => "Simple", - "params" => array( - "result" => 0, - "value" => "The appmonitor client is reachable.", - ), - ), - ) -); $sCfgfile='../../config/inc_projects_config.php'; +require_once $sCfgfile; + +include('general_include.php'); // ---------------------------------------------------------------------- -// config file +// config files // ---------------------------------------------------------------------- $oMonitor->addCheck( - array( + [ "name" => "read config file", "description" => "Check if config file is readable", - "check" => array( + "check" => [ "function" => "File", - "params" => array( + "params" => [ "filename" => $sCfgfile, "file" => true, "readable" => true, - ), - ), - ) + ], + ], + ] ); -require_once $sCfgfile; + + +foreach ([ + "config/config_custom.php", + "config/config_defaults.php", + "config/inc_roles.php", +] as $sCfgfile) { + + $oMonitor->addCheck( + [ + "name" => "read config file $sCfgfile", + "description" => "Check if config file $sCfgfile is an existing file and is readable", + "check" => [ + "function" => "File", + "params" => [ + "filename" => "../../$sCfgfile", + "file" => true, + "readable" => true, + ], + ], + ] + ); +} + // echo '<pre>' . print_r($aConfig, 1) . '</pre>';die(); @@ -48,50 +71,50 @@ require_once $sCfgfile; // directories // ---------------------------------------------------------------------- -foreach (array( +foreach ([ - 'tmpDir'=>array('dir'=>$aConfig['tmpDir'], 'descr'=>'Temp Dir mit git Daten'), - 'configDir'=>array('dir'=>$aConfig['configDir'], 'descr'=>'Ablage der Programm-Config'), - 'dataDir'=>array('dir'=>$aConfig['dataDir'], 'descr'=>'Basisverzeichnis fue DB, Projekt-Configs, SSH-Keys'), - 'dataDir/database'=>array('dir'=>$aConfig['dataDir'].'/database', 'descr'=>'DB-Ablage (Sqlite)'), - 'dataDir/projects'=>array('dir'=>$aConfig['dataDir'].'/projects', 'descr'=>'Projekt-Configdateien'), - 'dataDir/sshkeys'=>array('dir'=>$aConfig['dataDir'].'/sshkeys', 'descr'=>'SSH Keys'), + 'tmpDir'=>['dir'=>$aConfig['tmpDir'], 'descr'=>'Temp Dir mit git Daten'], + 'configDir'=>['dir'=>$aConfig['configDir'], 'descr'=>'Ablage der Programm-Config'], + 'dataDir'=>['dir'=>$aConfig['dataDir'], 'descr'=>'Basisverzeichnis fue DB, Projekt-Configs, SSH-Keys'], + 'dataDir/database'=>['dir'=>$aConfig['dataDir'].'/database', 'descr'=>'DB-Ablage (Sqlite)'], + 'dataDir/projects'=>['dir'=>$aConfig['dataDir'].'/projects', 'descr'=>'Projekt-Configdateien'], + 'dataDir/sshkeys'=>['dir'=>$aConfig['dataDir'].'/sshkeys', 'descr'=>'SSH Keys'], - 'buildDir'=>array('dir'=>$aConfig['buildDir'], 'descr'=>'Basisverzeichnis fuer Builds'), - 'packageDir'=>array('dir'=>$aConfig['packageDir'], 'descr'=>'Basisverzeichnis der Pakete und Versionen'), - 'archiveDir'=>array('dir'=>$aConfig['archiveDir'], 'descr'=>'Ablage der gebuildeten Archive'), + 'buildDir'=>['dir'=>$aConfig['buildDir'], 'descr'=>'Basisverzeichnis fuer Builds'], + 'packageDir'=>['dir'=>$aConfig['packageDir'], 'descr'=>'Basisverzeichnis der Pakete und Versionen'], + 'archiveDir'=>['dir'=>$aConfig['archiveDir'], 'descr'=>'Ablage der gebuildeten Archive'], -) as $sKey=>$aItem) { +] as $sKey=>$aItem) { $oMonitor->addCheck( - array( + [ "name" => "dir $sKey", "description" => $aItem['descr'], "parent" => "read config file", - "check" => array( + "check" => [ "function" => "File", - "params" => array( + "params" => [ "filename" => $aItem['dir'], "dir" => true, "writable" => true, - ), - ), - ) + ], + ], + ] ); } $oMonitor->addCheck( - array( + [ "name" => "Free space in Archive dir ", "description" => "The file storage must have some space left", - "check" => array( + "check" => [ "function" => "Diskfree", "parent" => "read config file", - "params" => array( + "params" => [ "directory" => $aConfig['archiveDir'], "warning" => "2GB", "critical" => "500MB", - ), - ), - ) + ], + ], + ] ); // ---------------------------------------------------------------------- // count of Projects @@ -113,55 +136,55 @@ foreach ($oPrj->getProjects() as $sPrj) { $iInQueue+=$aProgress['hasQueue'] ? 1 : 0; } $oMonitor->addCheck( - array( + [ "name" => "ci projects", "description" => "Count of Projects in CI Webgui", "group" => "monitor", "parent" => "read config file", - "check" => array( + "check" => [ "function" => "Simple", - "params" => array( + "params" => [ "result" => $iProjectCount ? RESULT_OK : RESULT_ERROR, "value" => "found projects: $iProjectCount", "count" => $iProjectCount, "visual" => "simple", - ), - ), - ) + ], + ], + ] ); $oMonitor->addCheck( - array( + [ "name" => "ci inProgress", "description" => "Projects in progress", "group" => "monitor", "parent" => "read config file", - "check" => array( + "check" => [ "function" => "Simple", - "params" => array( + "params" => [ "result" => RESULT_OK, "value" => "found projects: $iInProgress", "count" => $iInProgress, "visual" => "simple", - ), - ), - ) + ], + ], + ] ); $oMonitor->addCheck( - array( + [ "name" => "ci hasqueue", "description" => "Waiting for install", "group" => "monitor", "parent" => "read config file", - "check" => array( + "check" => [ "function" => "Simple", - "params" => array( + "params" => [ "result" => RESULT_OK, "value" => "found projects: $iInQueue", "count" => $iInQueue, "visual" => "simple", - ), - ), - ) + ], + ], + ] ); include 'plugins/apps/shared_check_ssl.php'; @@ -176,32 +199,32 @@ if(isset($aConfig['foreman']['api'])){ require_once(__DIR__.'/../deployment/classes/foremanapi.class.php'); $oForeman = new ForemanApi($aConfig['foreman']); - foreach (array('hostgroups', 'hosts') as $sForemanKey){ - $aFData=$oForeman->read(array( - 'request' => array( - array($sForemanKey), - ), - 'response' => array( + foreach (['hostgroups', 'hosts'] as $sForemanKey){ + $aFData=$oForeman->read([ + 'request' => [ + [$sForemanKey], + ], + 'response' => [ 'id', 'title' - ), - )); + ], + ]); $oMonitor->addCheck( - array( + [ "name" => "Foreman $sForemanKey", "description" => "Count of Foreman $sForemanKey", "group" => "monitor", "parent" => "read config file", - "check" => array( + "check" => [ "function" => "Simple", - "params" => array( + "params" => [ "result" => count($aFData) ? RESULT_OK : RESULT_ERROR, "value" => "Count of found Foreman $sForemanKey: " . count($aFData)."; response status: ".$oForeman->getResponseStatus() . "; Http-Code ".$oForeman->getResponseInfo('http_code'), "count" => count($aFData), "visual" => "simple", - ), - ), - ) + ], + ], + ] ); } } @@ -220,16 +243,16 @@ if(isset($aConfig['plugins']['rollout']['awx'])){ } $oMonitor->addCheck( - array( + [ "name" => "AWX API", "description" => "check if AWX api is available", "group" => "network", "parent" => "read config file", - "check" => array( + "check" => [ "function" => "HttpContent", "params" => $aOpts, - ), - ) + ], + ] ); } @@ -252,56 +275,56 @@ if(count($aConfig['mirrorPackages'])){ exec($sCmd, $sOut, $iRc); $oMonitor->addCheck( - array( + [ "name" => "mirror target $sHostKey", "description" => "Sync target of generated packages", "group" => "network", "parent" => "read config file", - "check" => array( + "check" => [ "function" => "Simple", - "params" => array( + "params" => [ "result" => $iRc ? RESULT_ERROR : RESULT_OK, "value" => "Command [$sCmd] returns with exitcode $iRc; output: ".implode(" ", $sOut), - ), - ), - ) + ], + ], + ] ); } } -foreach(array( +foreach([ /* - array( + [ 'host'=>'gitlab.iml.unibe.ch', 'port'=>22, 'descr'=>'SSH port to Gitlab on gitlab.iml.unibe.ch' - ), + ], */ - array( + [ 'host'=>'git-repo.iml.unibe.ch', 'port'=>22, 'descr'=>'SSH port to Gitlab on git-repo.iml.unibe.ch' - ), - array( + ], + [ 'host'=>'github.com', 'port'=>22, 'descr'=>'SSH port to Github' - ), -) as $aDescr){ + ], +] as $aDescr){ $oMonitor->addCheck( - array( - "name" => 'port check '.$aDescr['host'].':'.$aDescr['port'], - "description" => $aDescr['descr'], - "group" => "cloud", - "parent" => "read config file", - "check" => array( - "function" => "PortTcp", - "params" => array( - "port"=>$aDescr['port'], - "host"=>$aDescr['host'], - ), - ), - ) + [ + "name" => 'port check '.$aDescr['host'].':'.$aDescr['port'], + "description" => $aDescr['descr'], + "group" => "cloud", + "parent" => "read config file", + "check" => [ + "function" => "PortTcp", + "params" => [ + "port"=>$aDescr['port'], + "host"=>$aDescr['host'], + ], + ], + ] ); } @@ -312,56 +335,56 @@ foreach(array( $sSqlitefile=$aConfig['dataDir'].'/database/logs.db'; $oMonitor->addCheck( - array( + [ "name" => "Sqlite DB for action logs", "description" => "Connect sqlite db ". basename($sSqlitefile), "parent" => "read config file", - "check" => array( + "check" => [ "function" => "SqliteConnect", - "params" => array( + "params" => [ "db"=>$sSqlitefile - ), - ), - ) + ], + ], + ] ); // ---------------------------------------------------------------------- // system stuff // ---------------------------------------------------------------------- $oMonitor->addCheck( - array( + [ "name" => "plugin Load", "description" => "current load", "parent" => false, - "check" => array( + "check" => [ "function" => "Loadmeter", - "params" => array( + "params" => [ "warning" => 1.0, "error" => 3, - ), - ), + ], + ], "worstresult" => RESULT_OK - ) + ] ); $oMonitor->addCheck( - array( + [ "name" => "plugin ApacheProcesses", "description" => "Apache processes", "parent" => false, - "check" => array( + "check" => [ "function" => "ApacheProcesses", - "params" => array( + "params" => [ "warning" => 50, "error" => 75, - ), - ), + ], + ], "worstresult" => RESULT_OK - ) + ] ); +// ---------------------------------------------------------------------- -// Gesamt-Ergebnis - ohne Param=aut. max. Wert nehmen $oMonitor->setResult(); - -// Ausgabe $oMonitor->render(); + +// ---------------------------------------------------------------------- diff --git a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php index 0c468221f0620aaf3a9b600fe3d3f1099511d2e7..01797f877e280adcc54f4d99812c930f8c18c112 100755 --- a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php +++ b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php @@ -163,6 +163,24 @@ $oMonitor->addCheck( // ---------------------------------------------------------------------- include 'shared_check_ssl.php'; + +$oMonitor->addCheck( + [ + "name" => "plugin Load", + "description" => "current load", + "group" => 'monitor', + "parent" => false, + "check" => [ + "function" => "Loadmeter", + "params" => [ + "warning" => 1.0, + "error" => 3, + ], + ], + "worstresult" => RESULT_OK + ] +); + // ---------------------------------------------------------------------- // plugin test // ---------------------------------------------------------------------- diff --git a/public_html/appmonitor/plugins/apps/inc_appcheck_end.php b/public_html/appmonitor/plugins/apps/inc_appcheck_end.php new file mode 100644 index 0000000000000000000000000000000000000000..5f958628cffe7fb80936cf50bc333c5e032b39e6 --- /dev/null +++ b/public_html/appmonitor/plugins/apps/inc_appcheck_end.php @@ -0,0 +1,15 @@ +<?php +/* ______________________________________________________________________ + * + * A P P M O N I T O R :: CLIENT - INCLUDE FOR APP CHECKS :: ON END + * ______________________________________________________________________ + */ + +// $bStandalone was set in inc_appcheck_start.php +// send response if client was not initialized there +if($bStandalone){ + $oMonitor->setResult(); + $oMonitor->render(); +} + +// ---------------------------------------------------------------------- diff --git a/public_html/appmonitor/plugins/apps/inc_appcheck_start.php b/public_html/appmonitor/plugins/apps/inc_appcheck_start.php new file mode 100644 index 0000000000000000000000000000000000000000..93e17e635c07e66aa8b2680cfb39e8d5738e3472 --- /dev/null +++ b/public_html/appmonitor/plugins/apps/inc_appcheck_start.php @@ -0,0 +1,40 @@ +<?php +/* ______________________________________________________________________ + * + * A P P M O N I T O R :: CLIENT - INCLUDE FOR APP CHECKS :: ON START + * ______________________________________________________________________ + */ + +// ---------------------------------------------------------------------- +// CHECK IF THE APPROOT IS SET +// ---------------------------------------------------------------------- + +if (!$sApproot) { + header('HTTP/1.0 503 Service Unavailable'); + echo "<h1>503 Service Unavailable</h1>"; + echo 'ERROR:'.PHP_EOL; + echo '$sApproot was not set. Define it before including the application check.'.PHP_EOL; + echo 'Set the base folder of your application installation.'.PHP_EOL; + echo PHP_EOL; + echo 'Example:'.PHP_EOL; + echo '$sApproot = $_SERVER[\'DOCUMENT_ROOT\'];'.PHP_EOL; + echo '$sApproot = $_SERVER[\'DOCUMENT_ROOT\'].\'/myapp\';'.PHP_EOL; + die(); +} + +// initialize client and set very basic metadata ... if needed +$bStandalone=!(class_exists('appmonitor') && isset($oMonitor)); +if($bStandalone){ + require_once(__DIR__.'/../../classes/appmonitor-client.class.php'); + $oMonitor = new appmonitor(); + $oMonitor->setWebsite('Wordpress Instance'); + + @include __DIR__.'/../../general_include.php'; +} + +// ---------------------------------------------------------------------- +// FUNCTIONS +// ---------------------------------------------------------------------- + + +// ---------------------------------------------------------------------- diff --git a/public_html/appmonitor/plugins/apps/wordpress.php b/public_html/appmonitor/plugins/apps/wordpress.php new file mode 100644 index 0000000000000000000000000000000000000000..711a15fcc7bbc64f564f95cd07c6e7e9cba9a643 --- /dev/null +++ b/public_html/appmonitor/plugins/apps/wordpress.php @@ -0,0 +1,101 @@ +<?php +/* ______________________________________________________________________ + * + * WORK IN PROGRESS + * + * A P P M O N I T O R :: CLIENT - CHECK + * ______________________________________________________________________ + * + * Check for a Wordpress instance. + * Blogsoftware https://wordpress.org/ + * + * It checks + * - the write access to the config file + * - connect to mysql database (which is read from config) + * - ssl certificate (on https request only) + * + * @author: <axel.hahn@unibe.ch> + * ---------------------------------------------------------------------- + * 2018-11-07 v0.01 + * 2019-05-24 v0.02 detect include or standalone mode + * 2019-05-24 v0.03 detect include or standalone mode + * 2024-07-31 v0.04 first version for wordpress check in plugins/apps/ + */ + + +require 'inc_appcheck_start.php'; + +// ---------------------------------------------------------------------- +// Read config items +// ---------------------------------------------------------------------- + +$sConfigfile = $sApproot . '/wp-config.php'; +if (!file_exists($sConfigfile)) { + header('HTTP/1.0 503 Service Unavailable'); + die('ERROR: Config file [wp-config.php] was not found. Set a correct $sApproot pointing to wordpress install dir.'); +} + +require($sConfigfile); +$aDb=[ + 'server' => DB_HOST, + 'username' => DB_USER, + 'password' => DB_PASSWORD, + 'database' => DB_NAME, + // 'port' => ??, +]; + +// ---------------------------------------------------------------------- +// checks +// ---------------------------------------------------------------------- + +$oMonitor->addCheck( + [ + "name" => "config file", + "description" => "The config file must be writable", + "check" => [ + "function" => "File", + "params" => [ + "filename" => $sConfigfile, + "file" => true, + "readable" => true, + "writable" => true, + ], + ], + ] +); + +$oMonitor->addCheck( + [ + "name" => "Mysql Connect", + "description" => "Connect mysql server " . $aDb['server'] . " as user " . $aDb['username'] . " to scheme " . $aDb['database'], + "parent" => "config file", + "check" => [ + "function" => "MysqlConnect", + "params" => [ + "server" => $aDb['server'], + "user" => $aDb['username'], + "password" => $aDb['password'], + "db" => $aDb['database'], + // "port" => $aDb['port'], + ], + ], + ] +); + +if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']){ + $oMonitor->addCheck( + [ + "name" => "Certificate check", + "description" => "Check if SSL cert is valid and does not expire soon", + "check" => [ + "function" => "Cert", + ], + ] + ); +} + +// ---------------------------------------------------------------------- + +require 'inc_appcheck_end.php'; + +// ---------------------------------------------------------------------- diff --git a/public_html/appmonitor/tests/test_plugins.php b/public_html/appmonitor/tests/test_plugins.php new file mode 100644 index 0000000000000000000000000000000000000000..bd62fa4f6eb962052d989d27840f381d2493e6f6 --- /dev/null +++ b/public_html/appmonitor/tests/test_plugins.php @@ -0,0 +1,48 @@ +<?php +/* + TEST CLIENT CHECKS +*/ + +// ---------------------------------------------------------------------- +// INIT +$sApproot = str_replace('\\', '/', dirname(__DIR__)); + + +// require_once(__DIR__.'/../classes/client_all_in_one.php'); +// echo "OK: file client_all_in_one.php was loaded\n"; + +require_once(__DIR__.'/../classes/appmonitor-client.class.php'); + +$oMonitor = new appmonitor(); + +$oMonitor->listChecks(); + + +echo "OK: class appmonitor was initialized\n"; + + +// ---------------------------------------------------------------------- +$oMonitor->addTag('monitoring'); + +// ---------------------------------------------------------------------- +$oMonitor->addCheck( + [ + "name" => "check config subdir", + "description" => "Check config target directory", + "check" => [ + "function" => "File", + "params" => [ + "filename" => $sApproot . "/server/config", + "dir" => true, + "writable" => true, + ], + ], + ] +); +echo "OK: the plugin File check was added.\n"; + +// ---------------------------------------------------------------------- +$oMonitor->setResult(); +echo "OK: setResult() was executed.\n"; + +// ---------------------------------------------------------------------- diff --git a/public_html/check-config.php b/public_html/check-config.php index e66c1f4f3002eaad0ce37295cb508b7223f683a3..2b1de42cee481b48ee57a218d71c86c78c8f1591 100644 --- a/public_html/check-config.php +++ b/public_html/check-config.php @@ -1,3 +1,17 @@ +<!DOCTYPE html> +<html> +<head> +<style> + body{font-family: arial; background: #f8f8f8; color:#345;} + .box{border: 2px solid rgba(0,0,0,0.2); padding: 1em; margin-bottom: 1em;} + .ok{color:#383; background:#cfd;} + .error{color:#833; background:#fdd;} +</style> +</head> +<body> + +<h1>CI Server :: Config Check</h1> + <?php /* ###################################################################### @@ -10,120 +24,137 @@ ###################################################################### */ - // ---------------------------------------------------------------------- // functions // ---------------------------------------------------------------------- /** - * get html code to draw a colored box + * Get html code to draw a colored box + * * @param string $s message * @param string $sClass css class; one of ok|info|error * @return string */ -function drawbox($s, $sClass) { - return '<div class="box ' . $sClass . '">' . $s . '</div>'; +function drawbox(string $s, string $sClass): string +{ + return "<div class=\"box $sClass\">$s</div>"; } /** - * show an alert box with error message - * @param type $s - * @return type + * Show an alert box with error message + * + * @param string $s message to show + * @return string */ -function showerror($s) { +function showerror($s) +{ return drawbox($s, 'error'); } /** - * show green box with ok message - * @param type $s - * @return type + * Show green box with ok message + * + * @param string $s message to show + * @return string */ -function showok($s) { +function showok($s) +{ return drawbox($s, 'ok'); } /** - * check a directory if it exists and is writtable + * Check a directory if it exists and is writtable + * * @global array $aErrors found errors + * * @param string $sDir directory to check - * @param string $sKey key in config (for output only) - * @return boolean + * @param string $sKey key in config where this directory is defined (for error output only) + * @return string */ -function checkdir($sDir, $sKey = false) { +function checkdir(string $sDir, string $sKey = ''): string +{ global $aErrors; + $sReturn = ''; - echo 'check directory [' . $sDir . '] '; + $sReturn.="Check directory [$sDir] "; if (!is_dir($sDir)) { - echo '<span class="error">does not exist</span><br>'; - $aErrors[] = "* \$aConfig['$sKey'] points to a non existing directory (" . $sDir . ").\n"; - return false; + $sReturn.='<span class="error">does not exist</span><br>'; + $aErrors[] = "* \$aConfig['$sKey'] points to a non existing directory ($sDir).\n"; } else { if (!is_writable($sDir)) { - echo '<span class="error">not writable</span><br>'; - $aErrors[] = "* \$aConfig['$sKey'] = " . $sDir . " is NOT writable.\n"; - return false; + $sReturn.='<span class="error">not writable</span><br>'; + $aErrors[] = "* \$aConfig['$sKey'] = $sDir is NOT writable.\n"; } else { - echo '<span class="ok">OK</span> exists and is writable<br>'; - return true; + $sReturn.='<span class="ok">OK</span> exists and is writable<br>'; } } - return false; + return $sReturn; } /** - * check if a php module was found + * Check if a php module was found + * * @global array $aErrors found errors + * * @param array $aRequiredMods array with needed php modules; default: empty = list all current php modules - * @return boolean + * @return string */ -function checkModule($aRequiredMods=array()){ +function checkModule($aRequiredMods = []): string +{ global $aErrors; - $sReturn=''; - - $aAllMods=get_loaded_extensions(false); + $sReturn = ''; + + $aAllMods = get_loaded_extensions(false); asort($aAllMods); - if(!count($aRequiredMods)){ - echo '<strong>List of all installes php modules</strong><br>'; - foreach($aAllMods as $sMod){ - echo $sMod.'<br>'; + if (!count($aRequiredMods)) { + $sReturn.= '<strong>List of all installes php modules</strong><br>'; + foreach ($aAllMods as $sMod) { + $sReturn.= "$sMod<br>"; } } else { - foreach($aRequiredMods as $sMod){ - echo $sMod.' - '; - if(!array_search($sMod, $aAllMods)===false){ - echo '<span class="ok">OK</span> installed'; + foreach ($aRequiredMods as $sMod) { + $sReturn.="$sMod - "; + if (!array_search($sMod, $aAllMods) === false) { + $sReturn.='<span class="ok">OK</span> installed'; } else { - echo '<span class="error">does not exist</span>'; + $sReturn.='<span class="error">does not exist</span>'; $aErrors[] = "* php module $sMod was not found.\n"; } - echo '<br>'; + $sReturn.='<br>'; } } - return true; + return $sReturn; } /** - * check if a command can be executed + * Check if a command can be executed + * * @global array $aErrors found errors + * * @param array $aCommands array with key=command + value=description - * @return boolean + * @return string */ -function checkCommands($aCommands){ +function checkCommands(array $aCommands): string +{ global $aErrors; - foreach ($aCommands as $sCommand=>$sDescription){ - echo 'command ['.$sCommand.'] ('.$sDescription.') - '; - system("$sCommand", $iReturn); - - if($iReturn===0){ - echo '<span class="ok">OK</span> '; - } else { - echo '<span class="error">does not exist</span>'; - $aErrors[] = "* command $sCommand was not found or failed.\n"; - } - echo '<br>'; + $sReturn = ''; + + foreach ($aCommands as $sCommand => $sDescription) { + $sReturn.='command [' . $sCommand . '] (' . $sDescription . ') - '; + + $aOut=[]; + exec("$sCommand 2>&1", $aOut, $iReturn) . " "; + $sReturn.= implode("\n", $aOut) . "\n"; + + if ($iReturn === 0) { + $sReturn.='<span class="ok">OK</span> '; + } else { + $sReturn.='<span class="error">does not exist</span>'; + $aErrors[] = "* command $sCommand was not found or failed.\n"; + } + $sReturn.='<br>'; } - return true; + return $sReturn; } // ---------------------------------------------------------------------- @@ -132,113 +163,94 @@ function checkCommands($aCommands){ // // ---------------------------------------------------------------------- -echo ' -<style> - body{font-family: arial; background: #f8f8f8; color:#345;} - .box{border: 2px solid rgba(0,0,0,0.2); padding: 1em; margin-bottom: 1em;} - .ok{color:#383; background:#cfd;} - .error{color:#833; background:#fdd;} -</style> -<h1>Config Checker</h1> -'; +$sOut=""; -$aErrors = array(); +$aErrors = []; include("../config/inc_projects_config.php"); if (!isset($aConfig) || !is_array($aConfig)) { // echo showerror("\$aConfig does not exist. The config was not included before including " . __FILE__ . " in the request/ script."); $aErrors[] = "* \$aConfig does not exist. The config was not included before including " . __FILE__ . " in the request/ script."; } else { - + // ---------------------------------------------------------------------- - echo '<h2>Check keys with directories</h2>'; - foreach (array( - // 'appRootDir', - 'configDir', - 'workDir', - 'dataDir', - 'buildDir', - 'buildDefaultsDir', - 'packageDir', - 'archiveDir', - 'tmpDir', - ) as $sKey) { - checkdir($aConfig[$sKey], $sKey); + $sOut.='<h2>Check keys with directories</h2>'; + foreach ([ // 'appRootDir', + 'configDir', 'workDir', 'dataDir', 'buildDir', 'buildDefaultsDir', 'packageDir', 'archiveDir', 'tmpDir', ] as $sKey) { + $sOut.=checkdir($aConfig[$sKey], $sKey); } - - echo '<h2>Check subdirs of key dataDir</h2>'; - foreach (array( - $aConfig['dataDir'] . '/database', - $aConfig['dataDir'] . '/projects', - $aConfig['dataDir'] . '/sshkeys', - ) as $sDir2Check) { - checkdir($sDir2Check, 'dataDir + [subdir]'); + + $sOut.='<h2>Check subdirs of key dataDir</h2>'; + foreach ([$aConfig['dataDir'] . '/database', $aConfig['dataDir'] . '/projects', $aConfig['dataDir'] . '/sshkeys', ] as $sDir2Check) { + $sOut.=checkdir($sDir2Check, 'dataDir + [subdir]'); } - + // ---------------------------------------------------------------------- - echo '<h2>Check keys</h2>'; + $sOut.='<h2>Check keys</h2>'; // check required keys in the config - foreach (array( - 'auth', - 'build', - 'lang', - 'phases', - 'projects', - ) as $sKey) { - echo "Key [$sKey] "; + foreach (['auth', 'build', 'lang', 'phases', 'projects', ] as $sKey) { + $sOut.="Key [$sKey] "; if (!array_key_exists($sKey, $aConfig)) { - echo '<span class="error">failed</span> missing key ['.$sKey.'] in config<br>'; + $sOut.="<span class=\"error\">failed</span> missing key [$sKey] in config<br>"; $aErrors[] = "* missing key [$sKey] in config\n"; } else { - echo '<span class="ok">OK</span> exists<br>'; + $sOut.='<span class="ok">OK</span> exists<br>'; } } } -echo '<h2>Check required PHP modules</h2>'; -echo 'PHP version: '.PHP_VERSION.'<br>' - . '<br>'; -checkModule(array( - 'PDO','curl', 'json', 'ldap', 'pdo_sqlite' -)); +$sOut.='<h2>Check required PHP modules</h2>'; +$sOut.='PHP version: ' . PHP_VERSION . '<br>' + . '<br>'; +$sOut.=checkModule([ + 'PDO', + 'curl', + 'json', + 'ldap', + 'pdo_sqlite' +]); -echo '<h2>Check executable commands</h2>' +$sOut.='<h2>Check executable commands</h2>' . '<p>remark: if the apache user has no login shell then all commands will fail.</p>'; -echo '<h3>basic tools</h3>'; -checkCommands(array( - 'which bash'=>'shell interpreter to execute hook scripts', - 'which cat'=>'show content of files', - 'which cp'=>'copy files', - 'which ln'=>'create softlinks for version handling', - 'which ls'=>'list files', - 'which mkdir'=>'create working directories', - 'which mv'=>'move files', - 'which ps'=>'process list', - 'which rm'=>'remove files and directories', -)); -echo '<h3>more tools</h3>'; -checkCommands(array( - 'which ssh'=>'connect with ssh for remote execution', - 'which rsync'=>'sync packages to puppet master', - 'which git'=>'connect to git repositories', -)); - -echo '<h3>tools for IML projects</h3>'; -checkCommands(array( - 'which uglifyjs'=>'compress js', - 'which yui-compressor'=>'compress js, css', - 'which nodejs'=>'Nodejs - Javascript v8', - 'which yarn'=>'nodejs package manager', -)); +$sOut.='<h3>basic tools</h3>'; +$sOut.=checkCommands([ + 'which bash' => 'shell interpreter to execute hook scripts', + 'which cat' => 'show content of files', + 'which cp' => 'copy files', + 'which ln' => 'create softlinks for version handling', + 'which ls' => 'list files', + 'which mkdir' => 'create working directories', + 'which mv' => 'move files', + 'which ps' => 'process list', + 'which rm' => 'remove files and directories', +]); +$sOut.='<h3>More tools</h3>'; +$sOut.=checkCommands([ + 'which ssh' => 'connect with ssh for remote execution', + 'which rsync' => 'sync packages to puppet master', + 'which git' => 'connect to git repositories', +]); + +$sOut.='<h3>tools for IML projects</h3>'; +$sOut.=checkCommands([ + 'which uglifyjs' => 'compress js', + 'which yui-compressor' => 'compress js, css', + 'which nodejs' => 'Nodejs - Javascript v8', + 'which yarn' => 'nodejs package manager', +]); -echo '<h2>Result</h2>'; if (count($aErrors)) { - echo showerror("FATAL ERROR in config.<br>" . implode("<br>\n", $aErrors)); + echo showerror("ERRORS were found:<br>" . implode("<br>\n", $aErrors)); } else { echo showok('OK, check was successfully finished.'); } - +echo '<h2>Result</h2>'; +echo $sOut; // do not enable - it shows passwords... // echo 'DEBUG: <pre>' . print_r($aConfig, 1) . '</pre>'; + +?> +</body> +</html> diff --git a/public_html/deployment/classes/actionlog.class.php b/public_html/deployment/classes/actionlog.class.php index ac16f79425b963a1376287d9e32d5aed196839a1..021dfaa1af0d851f6af6b7283874915277d3bd2f 100644 --- a/public_html/deployment/classes/actionlog.class.php +++ b/public_html/deployment/classes/actionlog.class.php @@ -5,16 +5,46 @@ require_once 'user.class.php'; * class to log all project actions, ie. build, deploy etc. * * @author hahn + * + * 2024-08-23 v1.1 Axel Hahn php8 only; added variable types; short array syntax; remove glyphicons */ class Actionlog { - private $_dbfile = ''; - private $_aLoglevels = array("info", "warning", "error", "success"); // array of valid loglevels - private $_sIP = false; - private $_sUser = false; - private $_sProject = false; - private $_sCreate = ' + /** + * database file + * @var string + */ + private string $_dbfile = ''; + /** + * List of allowed loglevels + * @var array + */ + private array $_aLoglevels = ["info", "warning", "error", "success"]; // array of valid loglevels + + /** + * Ip address of the client + * @var string + */ + private string $_sIP = ''; + + /** + * Authenticated user on the client + * @var string + */ + private string $_sUser = ''; + + /** + * Current project + * @var string + */ + private string $_sProject = ''; + + /** + * Create statement for the database + * @var string + */ + private string $_sCreate = ' CREATE TABLE "logs" ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , `time` DATETIME, @@ -39,13 +69,14 @@ class Actionlog `message` TEXT )'; */ + /** - * constructor - sets internal environment variables and checks existence + * Constructor - sets internal environment variables and checks existence * of the database * @global array $aConfig settings * @param string $sProject project ID */ - public function __construct($sProject = false) + public function __construct(string $sProject = '') { global $aConfig; if (!isset($aConfig["appRootDir"])) { @@ -71,20 +102,22 @@ class Actionlog } /** - * create sqlite database - called in constructor if the file does not exist + * Create sqlite database - called in constructor if the file does not exist */ - private function _createDb() + private function _createDb(): bool|PDOstatement { echo "try to create file $this->_dbfile ...<br>\n"; return $this->_makeQuery($this->_sCreate); } /** - * execute a sql statement + * Execute a sql statement and get the result + * TODO: support prepared statements + * * @param string $sSql sql statement - * @return database object + * @return bool|PDO statement */ - private function _makeQuery($sSql) + private function _makeQuery(string $sSql): bool|PDOstatement { // $this->_log(__FUNCTION__."($sSql)"); // echo "<pre>".htmlentities($sSql)."</pre>"; @@ -100,16 +133,18 @@ class Actionlog } /** - * add a log message + * Add a log message + * TODO: supoport prepared statements + * * @param string $sMessage message * @param string $sAction project action; i.e. build, deploy, ... * @param string $sLoglevel loglevel - * @return type + * @return bool|PDOstatement */ - public function add($sMessage, $sAction = "", $sLoglevel = "info", $sTimeStart = false) + public function add(string $sMessage, string $sAction = "", string $sLoglevel = "info" /*, $sTimeStart = false */): bool|PDOstatement { if (array_search($sLoglevel, $this->_aLoglevels) === false) { - die(__class__ . ": loglevel $sLoglevel is invalid"); + die(__CLASS__ . ": loglevel $sLoglevel is invalid"); } $sql = "INSERT INTO `logs` (`time`, `loglevel`, `ip`, `user`, `project` ,`action`, `message`) VALUES( @@ -143,18 +178,18 @@ class Actionlog } /** - * helper function to remove chars in a string + * Helper function to remove chars in a string * @param string $sVal user value * @param string $sOKChars good chars to keep * @return string */ - private function _filterAllowedChars($sVal, $sOKChars) + private function _filterAllowedChars(string $sVal, string $sOKChars): string { return preg_replace('/[^' . $sOKChars . ']/i', '', $sVal); } /** - * get log data + * Get log data * @param array $aFilter with the following keys: * 'project' - filter by project; will be mixed with where (see next key) * 'from ' - time greater equal; time as string i.e. "2020-06-24" or "2020-06-24 11:00:00" @@ -163,15 +198,15 @@ class Actionlog * 'limit' - limit clausel - part behind "LIMIT " * @return array */ - public function getLogs($aFilter = array()) + public function getLogs(array $aFilter = []): array { // var_dump(R::findAll( 'log' )); - $aReturn = array(); + $aReturn = []; $sSql = 'SELECT `id`,`time`,`loglevel`,`ip`,`user`,`project`,`action`,`message` from logs '; $sWhere = false; - $aWhere = array(); + $aWhere = []; if (isset($aFilter["project"]) && $aFilter["project"]) { $aWhere[] = '`project`="' . $this->_filterAllowedChars($aFilter["project"], '[a-z0-9\-\_]') . '"'; } @@ -203,14 +238,14 @@ class Actionlog } /** - * render html code for a table with logs. The filter will be sent to + * Get html code for a table with logs. The filter will be sent to * getLogs method. * @param array $aFilter with the following keys: * 'project' - filter by project; will be mixed with where (see next key) * 'limit' - limit clausel - part behind "LIMIT " * @return string */ - public function renderLogs($aFilter = array(), $bIsFullsearch = false) + public function renderLogs(array $aFilter = [], bool $bIsFullsearch = false): string { $sReturn = ''; @@ -220,35 +255,36 @@ class Actionlog $bWasShown = true; require_once 'formgen.class.php'; + $oHtml = new htmlguielements(); // values for dropdowns - limit of lines; time - $aLimits = array( - '20' => array('label' => 20), - '50' => array('label' => 50), - '100' => array('label' => 100), - '' => array('label' => t("all")), - ); - $aTimes = array( - date("Y-m-d", date("U")) => array('label' => t("class-actionlog-time-today")), - date("Y-m-d", date("U") - 60 * 60 * 24 * 1) => array('label' => t("class-actionlog-time-since-yesterday")), - date("Y-m-d", date("U") - 60 * 60 * 24 * 7) => array('label' => t("class-actionlog-time-for-1-week")), - date("Y-m-d", date("U") - 60 * 60 * 24 * 7 * 2) => array('label' => sprintf(t("class-actionlog-time-for-n-weeks"), "2")), - date("Y-m-d", date("U") - 60 * 60 * 24 * 7 * 4) => array('label' => sprintf(t("class-actionlog-time-for-n-weeks"), "4")), - '' => array('label' => t("all")), - ); - - $aForms = array( - 'filter' => array( - 'meta' => array( + $aLimits = [ + '20' => ['label' => 20], + '50' => ['label' => 50], + '100' => ['label' => 100], + '' => ['label' => t("all")], + ]; + $aTimes = [ + date("Y-m-d", date("U")) => ['label' => t("class-actionlog-time-today")], + date("Y-m-d", date("U") - 60 * 60 * 24 * 1) => ['label' => t("class-actionlog-time-since-yesterday")], + date("Y-m-d", date("U") - 60 * 60 * 24 * 7) => ['label' => t("class-actionlog-time-for-1-week")], + date("Y-m-d", date("U") - 60 * 60 * 24 * 7 * 2) => ['label' => sprintf(t("class-actionlog-time-for-n-weeks"), "2")], + date("Y-m-d", date("U") - 60 * 60 * 24 * 7 * 4) => ['label' => sprintf(t("class-actionlog-time-for-n-weeks"), "4")], + '' => ['label' => t("all")], + ]; + + $aForms = [ + 'filter' => [ + 'meta' => [ 'method' => 'GET', 'action' => '?', 'class' => 'form-inline', - ), - 'validate' => array(), - 'form' => array(), - ) - ); + ], + 'validate' => [], + 'form' => [], + ] + ]; // generate filter for log table // $bIsFullsearch: true = show all inputs; false: no interactive search @@ -257,83 +293,83 @@ class Actionlog // --- list of all projects in log $sSql = 'SELECT DISTINCT(project) from `logs` order by project asc'; - $aForms["filter"]["form"]['selectproject'] = array( + $aForms["filter"]["form"]['selectproject'] = [ 'type' => 'select', 'name' => 'selectproject', - 'label' => '<i class="glyphicon glyphicon-tag"></i> ' . t('project'), + 'label' => $oHtml->getIcon('project') . ' ' . t('project'), 'class' => 'span2', 'onchange' => 'updateActionlog();', 'inline' => true, - ); - $aForms["filter"]["form"]['selectproject']['options'][''] = array('label' => t("all")); + ]; + $aForms["filter"]["form"]['selectproject']['options'][''] = ['label' => t("all")]; foreach ($this->_makeQuery($sSql) as $row) { if ($row[0]) { - $aForms["filter"]["form"]['selectproject']['options'][$row[0]] = array('label' => $row[0]); + $aForms["filter"]["form"]['selectproject']['options'][$row[0]] = ['label' => $row[0]]; } } - $aForms["filter"]["form"]['selectfrom'] = array( + $aForms["filter"]["form"]['selectfrom'] = [ 'type' => 'select', 'name' => 'selectWheretime', - 'label' => '<i class="glyphicon glyphicon-calendar"></i> ' . t("class-actionlog-time"), + 'label' => $oHtml->getIcon('calendar') . ' ' . t("class-actionlog-time"), 'class' => 'span2', 'onchange' => 'updateActionlog();', 'options' => $aTimes, 'inline' => true, - ); - $aForms["filter"]["form"]['selectlimit'] = array( + ]; + $aForms["filter"]["form"]['selectlimit'] = [ 'type' => 'select', 'name' => 'selectlimit', - 'label' => '<i class="glyphicon glyphicon-list"></i> ' . t("class-actionlog-count"), + 'label' => $oHtml->getIcon('list') . ' ' . t("class-actionlog-count"), 'class' => 'span1', 'onchange' => 'updateActionlog();', 'options' => $aLimits, 'inline' => true, - ); + ]; // force a line break - $aForms["filter"]["form"]['line'] = array( + $aForms["filter"]["form"]['line'] = [ 'type' => 'markup', 'value' => '<div class="col-sm-12"></div>', - ); + ]; } else { // write filters as hidden fields if (isset($aFilter["project"])) { - $aForms["filter"]["form"]['selectproject'] = array( + $aForms["filter"]["form"]['selectproject'] = [ 'type' => 'hidden', 'value' => $aFilter["project"] - ); + ]; } if (isset($aFilter["limit"])) { - $aForms["filter"]["form"]['selectlimit'] = array( + $aForms["filter"]["form"]['selectlimit'] = [ 'type' => 'hidden', 'value' => $aFilter["limit"] - ); + ]; } } - $aForms["filter"]["form"]['efilterlogs'] = array( + $aForms["filter"]["form"]['efilterlogs'] = [ 'type' => 'text', 'name' => 'efilterlogs', - 'label' => '<i class="glyphicon glyphicon-filter"></i>' . t("overview-textsearch"), + 'label' => $oHtml->getIcon('filter') . ' ' . t("overview-textsearch"), 'inline' => true, 'onkeyup' => 'filterLogTable();', 'onkeypress' => 'filterLogTable();', 'onchange' => 'filterLogTable();', //'size' => 20, - ); + ]; if (!$bIsFullsearch) { - $aForms["filter"]["form"]['btnalllogs'] = array( + $aForms["filter"]["form"]['btnalllogs'] = [ 'type' => 'button', 'value' => t('show all'), 'class' => 'btn btn-secondary btnlogs', 'href' => '/deployment/all/setup/actionlog/', 'onclick' => 'location.href=\'/deployment/all/setup/actionlog/\'; return false;', - ); + ]; } else { - // $aForms["filter"]["form"]['closediv'] = array( + // $aForms["filter"]["form"]['closediv'] = [ // 'type' => 'markup', // 'value' => '</div><!-- closediv -->', - // ); + // ]; } @@ -354,7 +390,7 @@ class Actionlog <button onclick="setTimelineVisibility(\'block\');" id="btnShowTimeline" class="btn btn-default btnTimeline"><i class="fa-solid fa-chevron-right"></i> </button> <button onclick="setTimelineVisibility(\'none\');" id="btnHideTimeline" class="btn btn-default btnTimeline"><i class="fa-solid fa-chevron-down"></i> </button> ' . t("class-actionlog-timeline") . (isset($aFilter["project"]) ? ' [' . $aFilter["project"] . '] ' : '') - .'<div id="divTimeline"></div> + . '<div id="divTimeline"></div> </div> <script> var sMsgNolog="' . t("class-actionlog-nolog") . '"; diff --git a/public_html/deployment/classes/base.class.php b/public_html/deployment/classes/base.class.php index cefb6605b88a9ef59a2eb029e0b16410a45f4bce..fdaa3789f7c0db8a0ad234cfb97dbde10ee51067 100644 --- a/public_html/deployment/classes/base.class.php +++ b/public_html/deployment/classes/base.class.php @@ -7,14 +7,15 @@ require_once 'user.class.php'; * * @author hahn */ -class base { +class base +{ /** - * logged in user - * @var object + * logged in user as user object + * @var user */ - var $oUser=false; - + public user $oUser; + /** * init user with optional given user * @param type $sUser @@ -22,5 +23,5 @@ class base { $this->oUser=new user(); } */ - + } diff --git a/public_html/deployment/classes/build.interface.php b/public_html/deployment/classes/build.interface.php index 9c9abf36cf15b5c69b65699c9d8829eaea719b4f..f9c0e6017076bbed54a2e7596ffebc36e0dfe054 100644 --- a/public_html/deployment/classes/build.interface.php +++ b/public_html/deployment/classes/build.interface.php @@ -4,20 +4,21 @@ * * @author hahn */ -interface iBuildplugin { - +interface iBuildplugin +{ + /** - * get an array of commands to check requirements + * Get an array of commands to check requirements * if the plugin is able to work * @return array */ - public function checkRequirements(); + public function checkRequirements(): array; /** - * get an array with shell commands to execute + * Get an array with shell commands to execute * @return array */ - public function getBuildCommands(); - + public function getBuildCommands(): array; + } diff --git a/public_html/deployment/classes/build_base.class.php b/public_html/deployment/classes/build_base.class.php index 723deb290e476fcf7cb8b1f746a2838277fd084f..29945461c3ac0c2949fd3ba6f688890cc28320a8 100644 --- a/public_html/deployment/classes/build_base.class.php +++ b/public_html/deployment/classes/build_base.class.php @@ -6,17 +6,47 @@ require_once 'build.interface.php'; * see deployment/plugins/build/* * * @author axel + * + * 2024-08-23 v1.1 Axel Hahn php8 only; added variable types; short array syntax */ -class build_base implements iBuildplugin{ - - protected $_sBuildDir = false; - protected $_sOutfile = false; - - protected $_sPluginId = false; - protected $_aPlugininfos = false; +class build_base implements iBuildplugin +{ - protected $_sLang = "en-en"; - protected $_aLang = []; + /** + * path of the build directory + * @var string + */ + protected string $_sBuildDir = ''; + + /** + * outputfile during build + * @var string + */ + protected string $_sOutfile = ''; + + /** + * id of the plugin + * @var string + */ + protected string $_sPluginId = ''; + + /** + * array with plugin infos + * @var array + */ + protected array $_aPlugininfos = []; + + /** + * language of ui; default is "en-en" + * @var string + */ + protected string $_sLang = "en-en"; + + /** + * array of language texts + * @var array + */ + protected array $_aLang = []; // --------------------------------------------------------------- @@ -24,58 +54,59 @@ class build_base implements iBuildplugin{ // --------------------------------------------------------------- /** - * initialize build plugin + * Constructor + * Initialize build plugin * @param array $aParams hash with those possible keys * lang string language, i.e. 'de' * phase string name of phase in a project * globalcfg array given global config $aConfig * projectcfg array project config to generate config * for project and all phases - * @return boolean */ - public function __construct($aParams) { - + public function __construct(array $aParams) + { + // set current plugin id - taken from plugin directory name above - $oReflection=new ReflectionClass($this); - $this->_sPluginId=basename(dirname($oReflection->getFileName())); - + $oReflection = new ReflectionClass($this); + $this->_sPluginId = basename(dirname($oReflection->getFileName())); + // ----- init language - if (isset($aParams['lang'])){ + if (isset($aParams['lang'])) { $this->setLang($aParams['lang']); } else { $this->setLang(); } - if (isset($aParams['workdir'])){ + if (isset($aParams['workdir'])) { $this->setWorkdir($aParams['workdir']); } - if (isset($aParams['outfile'])){ + if (isset($aParams['outfile'])) { $this->setOutfile($aParams['outfile']); } - return true; } - + // --------------------------------------------------------------- // LANGUAGE TEXTS // --------------------------------------------------------------- - + /** - * get a translated text from lang_XX.json in plugin dir; + * Get a translated text from lang_XX.json in plugin dir; * If the key is missed it returns "[KEY :: LANG]" * * @see setLang() * @param string $sKey key to find in lang file * @return string */ - protected function _t($sKey){ + protected function _t(string $sKey): string + { return (isset($this->_aLang[$sKey]) && $this->_aLang[$sKey]) - ? $this->_aLang[$sKey] - : "[ $sKey :: $this->_sLang ]" + ? $this->_aLang[$sKey] + : "[ $sKey :: $this->_sLang ]" ; } /** - * set language for output of formdata and other texts. + * Set language for output of formdata and other texts. * This method loads the language file into a hash. The output of * translated texts can be done with $this->_t("your_key") * @@ -83,12 +114,13 @@ class build_base implements iBuildplugin{ * @param string $sLang language code, i.e. "de" * @return boolean */ - public function setLang($sLang=false){ - $this->_sLang=$sLang ? $sLang : $this->_sLang; - - $oReflection=new ReflectionClass($this); - $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sLang.'.json'; - $this->_aLang=(file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang; + public function setLang(string $sLang = ''): bool + { + $this->_sLang = $sLang ? $sLang : $this->_sLang; + + $oReflection = new ReflectionClass($this); + $sFile = dirname($oReflection->getFileName()) . '/lang_' . $this->_sLang . '.json'; + $this->_aLang = (file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang; return true; } // --------------------------------------------------------------- @@ -97,123 +129,145 @@ class build_base implements iBuildplugin{ /** - * set build dir with sources + * Set build dir with sources * @param string $sBuildDir full path of the build directory - * @return array + * @return bool */ - public function setWorkdir($sBuildDir){ - return $this->_sBuildDir=$sBuildDir ? $sBuildDir : $this->_sBuildDir; + public function setWorkdir(string $sBuildDir): bool + { + $this->_sBuildDir = $sBuildDir ? $sBuildDir : $this->_sBuildDir; + return true; } /** - * set outfile name + * Set outfile name * @param string $sOutFilename filename for output (without extension) - * @return array + * @return bool */ - public function setOutfile($sOutFilename){ - return $this->_sOutfile=$sOutFilename ? $sOutFilename : $this->_sOutfile; + public function setOutfile(string $sOutFilename) + { + $this->_sOutfile = $sOutFilename ? $sOutFilename : $this->_sOutfile; + return true; } // --------------------------------------------------------------- // GETTER // --------------------------------------------------------------- - + /** - * check requirements if the plugin could work + * Get an array with shell commands to check requirements if the plugin + * can work + * * @return array */ - public function checkRequirements() { + public function checkRequirements(): array + { return [ - 'echo "ERROR: The method checkRequirements() was not implemented in the build plugin ['.$this->getId().']"', + 'echo "ERROR: The method checkRequirements() was not implemented in the build plugin [' . $this->getId() . ']"', 'exit 1' - ]; + ]; } /** - * get an array with shell commands to execute + * Get an array with shell commands to execute * @return array */ - public function getBuildCommands(){ + public function getBuildCommands(): array + { return [ - 'echo "ERROR: The method getBuildCommamds() was not implemented in the build plugin ['.$this->getId().']"', + 'echo "ERROR: The method getBuildCommamds() was not implemented in the build plugin [' . $this->getId() . ']"', 'exit 1' - ]; + ]; } - + /** - * get string with current ID + * Get string with current ID * @return string */ - public function getId(){ + public function getId(): string + { return $this->_sPluginId; } - + /** - * get string with plugin name (taken from plugin language file) + * Get string with plugin name (taken from plugin language file) * @return string */ - public function getName(){ + public function getName():string + { return $this->_t('plugin_name'); } - + /** - * get string with plugin description (taken from plugin language file) + * Get string with plugin description (taken from plugin language file) * @return string */ - public function getDescription(){ + public function getDescription(): string + { return $this->_t('description'); } + /** - * get array read from info.json - * @return type + * Get array read from info.json + * @return array */ - public function getPluginInfos(){ + public function getPluginInfos(): array + { - if ($this->_aPlugininfos){ + if ($this->_aPlugininfos) { return $this->_aPlugininfos; } - - $oReflection=new ReflectionClass($this); - $sFile=dirname($oReflection->getFileName()) . '/info.json'; - $this->_aPlugininfos= (file_exists($sFile)) + + $oReflection = new ReflectionClass($this); + $sFile = dirname($oReflection->getFileName()) . '/info.json'; + $this->_aPlugininfos = (file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) - : array('error'=> 'unable to read info file ['.$sFile.'].') + : ['error' => "unable to read info file [$sFile]."] ; return $this->_aPlugininfos; } /** - * get the file extension of created output file (from plugin info.json) + * Get the file extension of created output file (from plugin info.json) + * @return string */ - public function getExtension(){ - $aInfos=$this->getPluginInfos(); - return isset($aInfos['extension']) ? '.'.$aInfos['extension'] : ''; + public function getExtension(): string + { + $aInfos = $this->getPluginInfos(); + return isset($aInfos['extension']) ? '.' . $aInfos['extension'] : ''; } + /** - * set outfile name including extension (from plugin metadata) - * @param string $sOutFilename filename for output (without extension) - * @return array + * Get outfile name including extension (from plugin metadata) + * @return string */ - public function getOutfile(){ - return $this->_sOutfile.$this->getExtension(); + public function getOutfile(): string + { + return $this->_sOutfile . $this->getExtension(); } + /** - * set outfile name - * @param string $sOutFilename filename for output (without extension) - * @return array + * get current build dir + * @return string */ - public function getBuildDir(){ + public function getBuildDir(): string + { return $this->_sBuildDir; } // ---------------------------------------------------------------------- // INTERFACE :: RENDERER // ---------------------------------------------------------------------- - public function renderPluginBox(){ - $sReturn=''; - $aInfos=$this->getPluginInfos(); - return '<strong>'.$this->getName().'</strong> ('.$this->getId().')<br> - '.$this->getDescription(); + /** + * Render plugin box as HTML to show in plugin overview + * @return string + */ + public function renderPluginBox(): string + { + $aInfos = $this->getPluginInfos(); + + return '<strong>' . $this->getName() . '</strong> (' . $this->getId() . ')<br> + ' . $this->getDescription(); } } diff --git a/public_html/deployment/classes/classinfos.class.php b/public_html/deployment/classes/classinfos.class.php index 26ca2b02c38792f8725cd64eef766a45fdea73c9..8f1f7e9ec540e9665bac456f844105ac74ee020d 100644 --- a/public_html/deployment/classes/classinfos.class.php +++ b/public_html/deployment/classes/classinfos.class.php @@ -18,7 +18,6 @@ class classinfos { */ public function __construct($sClassname){ if($sClassname) $this->setClassname($sClassname); - return true; } public function setClassname($sClassname){ @@ -73,7 +72,7 @@ class classinfos { $sReturn=preg_replace('/\@('.$sKey.')/U', '<span class="doctag">@'.$sKey.'</span>', $sReturn); } return $sReturn; - return $sReturn='<div class="comment">'. $sReturn . '</div>'; + // return $sReturn='<div class="comment">'. $sReturn . '</div>'; } @@ -204,7 +203,7 @@ class classinfos { $source_code=file_get_contents($this->oRefClass->getFileName()); - $sHtml.='<div class="sourcecode">'; + $sHtml='<div class="sourcecode">'; if ($sMode=="html"){ $sHtml.='<h2 id="source">Source</h2>'; } @@ -343,7 +342,3 @@ class classinfos { } - -?> - - \ No newline at end of file diff --git a/public_html/deployment/classes/config-replacement.class.php b/public_html/deployment/classes/config-replacement.class.php index 93fb09a85b937b4526583168ff42687cdf247b2c..7b3f3d75eff17e1e77204c4f24f063111e806b7d 100644 --- a/public_html/deployment/classes/config-replacement.class.php +++ b/public_html/deployment/classes/config-replacement.class.php @@ -8,133 +8,153 @@ require_once 'project.class.php'; /** * config-replacement class * reads templatefiles and scans its placeholders for replacements - * + * + * @deprecated It is used for Foreman replacements; TODO: remove this class + * * @author hahn + * + * 2024-08-23 v1.1 Axel Hahn fix php parser problems */ -class configreplacement { - +class configreplacement +{ + /** * project class - * @var type + * @var object */ protected $_oProject = false; - protected $_sPhase = false; - protected $_aForemanReplacements = false; - /** - * init + * Phase of rollout + * @var string + */ + protected $_sPhase = ''; + + /** + * Found replacements in Foreman + * @var array + */ + protected array $_aForemanReplacements = []; + + + /** + * Constructor * @param string $sProject optional: project id; you can use setProject() too - * @return boolean */ - public function __construct($sProject = false) { - if ($sProject){ + public function __construct($sProject = '') + { + if ($sProject) { $this->setProject($sProject); } - return true; } - + /** - * get an array with a flat list of all templatefiles of a build + * Get an array with a flat list of all templatefiles of a build * @return boolean|array */ - public function getTemplatefiles(){ - if (!$this->_sPhase){ + public function getTemplatefiles(): bool|array + { + if (!$this->_sPhase) { return false; } - $aReturn = array(); + $aReturn = []; - $aBuildfiles=$this->_oProject->getBuildfilesByPlace($this->_sPhase, 'onhold'); - if (!$aBuildfiles){ - $aBuildfiles=$this->_oProject->getBuildfilesByPlace($this->_sPhase, 'ready2install'); + $aBuildfiles = $this->_oProject->getBuildfilesByPlace($this->_sPhase, 'onhold'); + if (!$aBuildfiles) { + $aBuildfiles = $this->_oProject->getBuildfilesByPlace($this->_sPhase, 'ready2install'); } - - if (!isset($aBuildfiles['types']['templates'])){ + + if (!isset($aBuildfiles['types']['templates'])) { return false; } - foreach ($aBuildfiles['types']['templates'] as $sFile){ - $aReturn[]=$aBuildfiles['dir'].'/'.$sFile; + foreach ($aBuildfiles['types']['templates'] as $sFile) { + $aReturn[] = $aBuildfiles['dir'] . '/' . $sFile; } return $aReturn; } /** * get an array with all template files (basename) and its replacement fields - * @return array + * @return bool|array */ - public function getReplacements(){ - if (!$this->_sPhase){ + public function getReplacements(): bool|array + { + if (!$this->_sPhase) { return false; } - $aFiles=$this->getTemplatefiles($this->_sPhase); - if (!$aFiles){ + $aFiles = $this->getTemplatefiles(); + if (!$aFiles) { return false; } - - $aReturn=array(); - foreach ($aFiles as $sFile){ + + $aReturn = []; + foreach ($aFiles as $sFile) { // $sFile always exists because it was read from filesystem - $sContent=file_get_contents($sFile); + $sContent = file_get_contents($sFile); preg_match_all('/\@replace\[[\'\"](.*)[\'\"]\]/U', $sContent, $aMatches); - $aReturn[$sFile]=$aMatches[1]; - } + $aReturn[$sFile] = $aMatches[1]; + } return $aReturn; } - + /** - * get effective hostgroup id of the current phase + * Get effective hostgroup id of the current phase * @return integer */ - public function getForemanHostgroup(){ - $aPrjConfig=$this->_oProject->getConfig(); + public function getForemanHostgroup(): int + { + $aPrjConfig = $this->_oProject->getConfig(); $iForemanHostgroupDefault = (int) $aPrjConfig['deploy']['foreman']['hostgroup']; $iForemanHostgroup = (int) $aPrjConfig['phases'][$this->_sPhase]['foreman-hostgroup']; - return ($iForemanHostgroup===OPTION_DEFAULT) ? $iForemanHostgroupDefault : $iForemanHostgroup; + return ($iForemanHostgroup === OPTION_DEFAULT) ? $iForemanHostgroupDefault : $iForemanHostgroup; } - + /** * get replacement definitions from foreman - * @global type $aConfig - * @return boolean + * + * @global array $aConfig + * + * @return bool|array */ - protected function _getForemanReplacement(){ + protected function _getForemanReplacement(): bool|array + { global $aConfig; - + $sProject = $this->_oProject->getId(); // echo "DEBUG: project id = $sProject<br>"; - - if (!$this->_sPhase){ + + if (!$this->_sPhase) { return false; } - + // abort if no foreman connection was configured if (!isset($aConfig['foreman'])) { return false; } // return already cached result - if (isset($this->_aForemanReplacements[$sProject])){ + if (isset($this->_aForemanReplacements[$sProject])) { return $this->_aForemanReplacements[$sProject]; } - + // rebuilt - $this->_aForemanReplacements[$sProject]=false; - $iEffectiveHostgroup=$this->getForemanHostgroup(); + $this->_aForemanReplacements[$sProject] = false; + $iEffectiveHostgroup = $this->getForemanHostgroup(); // abort if no hostgroup was set - if($iEffectiveHostgroup<=0){ + if ($iEffectiveHostgroup <= 0) { return false; } require_once 'foremanapi.class.php'; - $oForeman=new ForemanApi($aConfig['foreman']); - + $oForeman = new ForemanApi($aConfig['foreman']); + // get a host of this phase - $aHosts=$oForeman->read(array( + $aHosts = $oForeman->read(array( 'request' => array( - array('hosts' ), + array('hosts'), ), 'filter' => array( 'hostgroup_id' => $iEffectiveHostgroup, @@ -142,102 +162,103 @@ class configreplacement { 'response' => array('name', 'id'), )); - $sHost=''; - $aHostsOfPhase=array(); - + $sHost = ''; + $aHostsOfPhase = array(); + // HACK: phases are part of the hostname .. but not "live" ... and special handling for demo - $aPrjConfig=$this->_oProject->getConfig(); - $bIsDemo=(isset($aPrjConfig['fileprefix']) - && !strpos($aPrjConfig['fileprefix'], 'demo')===false); - $sPhase=$bIsDemo ? 'demo' : $this->_sPhase; - foreach($aHosts as $aName){ - $sName=$aName['name']; - if (($sPhase==='live' && - ( - strpos($sName, 'preview')===false - && strpos($sName, 'stage')===false - && strpos($sName, 'demo')===false + $aPrjConfig = $this->_oProject->getConfig(); + $bIsDemo = (isset($aPrjConfig['fileprefix']) + && !strpos($aPrjConfig['fileprefix'], 'demo') === false); + $sPhase = $bIsDemo ? 'demo' : $this->_sPhase; + foreach ($aHosts as $aName) { + $sName = $aName['name']; + if ( + ($sPhase === 'live' && + ( + strpos($sName, 'preview') === false + && strpos($sName, 'stage') === false + && strpos($sName, 'demo') === false + ) ) - ) - || (strpos($sName, $sPhase)) - ){ - $sHost=$sHost ? $sHost : $sName; - $aHostsOfPhase[]=$sName; + || (strpos($sName, $sPhase)) + ) { + $sHost = $sHost ? $sHost : $sName; + $aHostsOfPhase[] = $sName; } } - + // get created yaml of this host - $sUrl=$aConfig['foreman']['api']."hosts/$sHost/externalNodes?name=$sHost"; - $aData=$oForeman->makeRequest(array( - 'url'=>$sUrl, - 'method'=>'GET', + $sUrl = $aConfig['foreman']['api'] . "hosts/$sHost/externalNodes?name=$sHost"; + $aData = $oForeman->makeRequest(array( + 'url' => $sUrl, + 'method' => 'GET', )); - + // HACK: minify YAML of the host ... because it is too large for SPYC parser? - $sPart="---\n"; - $bCopy=false; - foreach(explode("\n", $aData['body']) as $sLine){ - if($bCopy){ - if (strpos($sLine, ' ')===false){ - $bCopy=false; + $sPart = "---\n"; + $bCopy = false; + foreach (explode("\n", $aData['body']) as $sLine) { + if ($bCopy) { + if (strpos($sLine, ' ') === false) { + $bCopy = false; } else { // remove leading spaces and html entities... - $sNewLine= html_entity_decode(preg_replace('/^\ \ \ \ /', '', $sLine, 1)); - $sNewLine=str_replace(''{', "'{", $sNewLine); - $sNewLine=str_replace('}'', "}'", $sNewLine); - + $sNewLine = html_entity_decode(preg_replace('/^\ \ \ \ /', '', $sLine, 1)); + $sNewLine = str_replace(''{', "'{", $sNewLine); + $sNewLine = str_replace('}'', "}'", $sNewLine); + // fix json errors - $sNewLine=str_replace(', }', " }", $sNewLine); - $sNewLine=str_replace(',}', "}", $sNewLine); - $sPart.=$sNewLine."\n"; + $sNewLine = str_replace(', }', " }", $sNewLine); + $sNewLine = str_replace(',}', "}", $sNewLine); + $sPart .= $sNewLine . "\n"; } } - if($sLine===' iml-deployment-config:'){ - $bCopy=true; + if ($sLine === ' iml-deployment-config:') { + $bCopy = true; } // echo 'DEBUG: '.($bCopy ? 'COPY':'SKIP').$sLine.'<br>'; } - if (strstr($sPart, '|-')){ + if (strstr($sPart, '|-')) { echo 'WARNING: the chars "|-" were found:<br><pre> ' - .str_replace('|-', '<span class="replace">|-</span>', $sPart) - .'</pre><br>'; + . str_replace('|-', '<span class="replace">|-</span>', $sPart) + . '</pre><br>'; } - require_once __DIR__.'./../../vendor/spyc/spyc.php'; - + require_once __DIR__ . './../../vendor/spyc/spyc.php'; + // echo 'DEBUG: <pre>'.print_r(spyc_load($aData['body']), 1).'</pre>'; - $aYaml=spyc_load($sPart); + $aYaml = spyc_load($sPart); // echo 'DEBUG: <pre>'.print_r($aYaml, 1).'</pre>'; - - if (!isset($aYaml[$sProject])){ + + if (!isset($aYaml[$sProject])) { return false; } - foreach ($aYaml as $sPrjId=>$aProject){ - $aReturn=array(); - foreach ($aProject as $sFile=>$aParams){ - $aReturn[$sFile]=array(); - if (isset($aParams['target'])){ - $aReturn[$sFile]['target']=$aParams['target']; + foreach ($aYaml as $sPrjId => $aProject) { + $aReturn = array(); + foreach ($aProject as $sFile => $aParams) { + $aReturn[$sFile] = array(); + if (isset($aParams['target'])) { + $aReturn[$sFile]['target'] = $aParams['target']; } - if (isset($aParams['replace'])){ - $aReplace=json_decode($aParams['replace'], 1); - $aReturn[$sFile]['replace_source']=$aReplace; - foreach ($aReplace as $sVarname=>$value){ - if (isset($value['_'.$this->_sPhase.'_'])){ - $value=$value['_'.$this->_sPhase.'_']; + if (isset($aParams['replace'])) { + $aReplace = json_decode($aParams['replace'], 1); + $aReturn[$sFile]['replace_source'] = $aReplace; + foreach ($aReplace as $sVarname => $value) { + if (isset($value['_' . $this->_sPhase . '_'])) { + $value = $value['_' . $this->_sPhase . '_']; } - $aReturn[$sFile]['replace'][$sVarname]=$value; + $aReturn[$sFile]['replace'][$sVarname] = $value; } } } - $this->_aForemanReplacements[$sPrjId]=array( - 'phase'=>$this->_sPhase, - 'rules'=>$aReturn, - 'host'=>$sHost, - 'yaml'=>$sPart, - 'hostgroup'=>$iEffectiveHostgroup, - 'hostsall'=>$aHosts, - 'hostsphase'=>$aHostsOfPhase, + $this->_aForemanReplacements[$sPrjId] = array( + 'phase' => $this->_sPhase, + 'rules' => $aReturn, + 'host' => $sHost, + 'yaml' => $sPart, + 'hostgroup' => $iEffectiveHostgroup, + 'hostsall' => $aHosts, + 'hostsphase' => $aHostsOfPhase, ); } @@ -245,107 +266,117 @@ class configreplacement { } /** - * get foreman base url ... if foreman was activated in the setup + * Get foreman base url ... if foreman was activated in the setup * * @global array $aConfig ci config * @return string */ - private function _getForemanBaseUrl(){ + private function _getForemanBaseUrl(): string + { global $aConfig; return (isset($aConfig['foreman']['api'])) ? $aConfig['foreman']['api'] : false ; } - + /** - * get html code for links to edit each host of the current phase in foreman + * Get html code for links to edit each host of the current phase in foreman + * It returns false if foreman is not activated or there is no phase. * - * @return boolean + * @return bool|string */ - public function getForemanlink2Host(){ - $sForemanurl=$this->_getForemanBaseUrl(); - if (!$sForemanurl){ + public function getForemanlink2Host(): bool|string + { + $sForemanurl = $this->_getForemanBaseUrl(); + if (!$sForemanurl) { return false; } - $aTmp=$this->_getForemanReplacement(); - if (!isset($aTmp['hostsphase'])){ + $aTmp = $this->_getForemanReplacement(); + if (!isset($aTmp['hostsphase'])) { return false; } require_once 'htmlguielements.class.php'; - $oHtml=new htmlguielements(); - $sReturn=''; - foreach ($aTmp['hostsphase'] as $sHost){ - $sReturn.=$oHtml->getLinkButton(array( - 'href'=>$sForemanurl.'hosts/'.$aTmp['host'], - 'target'=>'_foreman', - 'title'=>t('edit'), - 'icon'=>'host', - 'label'=>t('host').' '.$aTmp['host'], - )).' '; + $oHtml = new htmlguielements(); + $sReturn = ''; + foreach ($aTmp['hostsphase'] as $sHost) { + $sReturn .= $oHtml->getLinkButton(array( + 'href' => $sForemanurl . 'hosts/' . $aTmp['host'], + 'target' => '_foreman', + 'title' => t('edit'), + 'icon' => 'host', + 'label' => t('host') . ' ' . $aTmp['host'], + )) . ' '; } return $sReturn; } /** - * get html code for a link to edit hostgroup in foreman + * Get html code for a link to edit hostgroup in foreman + * It returns false if foreman is not activated or there is no hostgroup in the project phase * - * @return boolean + * @return bool|string */ - public function getForemanlink2Hostgroup(){ - $iEffectiveHostgroup=$this->getForemanHostgroup(); - if($iEffectiveHostgroup<=0){ + public function getForemanlink2Hostgroup(): bool|string + { + $iEffectiveHostgroup = $this->getForemanHostgroup(); + if ($iEffectiveHostgroup <= 0) { return false; } - $sForemanurl=$this->_getForemanBaseUrl(); - if (!$sForemanurl){ + $sForemanurl = $this->_getForemanBaseUrl(); + if (!$sForemanurl) { return false; } require_once 'htmlguielements.class.php'; - $oHtml=new htmlguielements(); + $oHtml = new htmlguielements(); return $oHtml->getLinkButton(array( - 'href'=>$sForemanurl.'hostgroups/'.$iEffectiveHostgroup.'/edit', - 'target'=>'_foreman', - 'title'=>t('edit'), - 'icon'=>'hostgroup', - 'label'=>sprintf(t('foreman-hostgroup-id'), $iEffectiveHostgroup), + 'href' => $sForemanurl . 'hostgroups/' . $iEffectiveHostgroup . '/edit', + 'target' => '_foreman', + 'title' => t('edit'), + 'icon' => 'hostgroup', + 'label' => sprintf(t('foreman-hostgroup-id'), $iEffectiveHostgroup), )); - + } - + /** * get replacements in foreman - * @return type + * @return bool|array */ - public function getForemanReplacements(){ + public function getForemanReplacements(): bool|array + { return $this->_getForemanReplacement(); } - + /** * switch to a project - * @param type $sProject + * @param string $sProject project id + * @param string $sPhase optional: a phase; one of preview|stage|live + * @return boolean */ - public function setProject($sProject, $sPhase=false){ + public function setProject(string $sProject, string $sPhase = ''): bool + { $this->_oProject = new project($sProject); - $this->_aForemanReplacements=false; + $this->_aForemanReplacements = []; $this->setPhase($sPhase); return true; } - + /** - * set a phase of a project + * Set a phase of a project * @param string $sPhase name of valid phase * @return boolean */ - public function setPhase($sPhase=false){ - $this->_sPhase=false; - if (!$sPhase){ - $sPhase=$this->_oProject->getNextPhase(false); + public function setPhase(string $sPhase = ''): bool + { + $this->_sPhase = false; + if (!$sPhase) { + $sPhase = $this->_oProject->getNextPhase(false); } if (!$sPhase || !$this->_oProject->isActivePhase($sPhase)) { return false; } - $this->_sPhase=$sPhase; + $this->_sPhase = $sPhase; return true; } } diff --git a/public_html/deployment/classes/foremanapi.class.php b/public_html/deployment/classes/foremanapi.class.php index 1e7030289d78f96d96c5ae22b67e88429420f82e..4365b1a12907b2b53cec1d874663273a02a7db37 100644 --- a/public_html/deployment/classes/foremanapi.class.php +++ b/public_html/deployment/classes/foremanapi.class.php @@ -4,6 +4,8 @@ * * foreman access to API * + * @deprecated Foreman was too much hartcoded. TODO: remove this class + * * @example * in project class * $oForeman=new ForemanApi($this->_aConfig['foreman']); @@ -116,13 +118,13 @@ class ForemanApi { /** * last request - * @var type + * @var array */ protected $_aRequest=array(); /** * last response - * @var type + * @var array */ protected $_aResponse=array(); @@ -135,12 +137,9 @@ class ForemanApi { public function __construct($aCfg) { if(!isset($aCfg['api'])){ die("ERROR: class ".__CLASS__." must be initialized with an array containing api config for foreman."); - return false; } $this->_aCfg=$aCfg; - - return true; - } + } // ---------------------------------------------------------------------- // private functions @@ -159,10 +158,10 @@ class ForemanApi { /** * search url prefix in $this->_aAllowedUrls by given key - * @param type $sFunction - * @return type + * @param string $sFunction + * @return string */ - protected function _guessPrefixUrl($sFunction=false){ + protected function _guessPrefixUrl($sFunction=''){ $sReturn=''; /* if (!$sFunction){ @@ -232,7 +231,7 @@ class ForemanApi { * - postdata; for POST only * * @param array $aRequest arrayurl for Foreman API - * @return string + * @return array */ protected function _httpCall($aRequest=false, $iTimeout = 15) { if ($aRequest){ @@ -433,7 +432,7 @@ class ForemanApi { /** * check for missing config entries - * @return type + * @return bool */ public function selfcheck() { $sOut=''; @@ -498,7 +497,7 @@ class ForemanApi { * - list of keys, i.e. array('id', 'title') * * @param array $aOptions - * @return array + * @return bool|array */ public function read($aOptions){ $this->_aRequest=$aOptions; @@ -512,7 +511,7 @@ class ForemanApi { /** * TODO - * @param type $aOptions + * @param array $aOptions */ public function update($aOptions){ /* @@ -524,7 +523,7 @@ class ForemanApi { /** * TODO - * @param type $aOptions + * @param array $aOptions */ public function delete($aOptions){ /* @@ -573,7 +572,7 @@ class ForemanApi { [local_port] => 33906 ) * @param string $sKey get value of given key only - * @return any + * @return mixed */ public function getResponseInfo($sKey=false){ diff --git a/public_html/deployment/classes/formgen.class.php b/public_html/deployment/classes/formgen.class.php index f60936bbc3b09a84991cf4f6a5375ec6cad417bb..bd226b00d435c3452efc153997bd28adfa123324 100644 --- a/public_html/deployment/classes/formgen.class.php +++ b/public_html/deployment/classes/formgen.class.php @@ -9,130 +9,150 @@ feature complete. --------------------------------------------------------------------- - 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + Axel <axel.hahn@iml.unibe.ch> + 2013-11-08 Axel + 2024-08-23 v1.1 Axel php8 only; added variable types; short array syntax ###################################################################### */ -class formgen { +class formgen +{ - var $aForm = array(); + var $aForm = []; var $sRequired = ' <span title="Eingabe ist erforderlich"><span style="color:#c00;">*</span></span>'; /** - * constructor + * Constructor * @param array $aNewFormData - * @return boolean */ - public function __construct($aNewFormData = array()) { - if (is_array($aNewFormData) && count($aNewFormData)){ - return $this->setFormarray($aNewFormData); + public function __construct($aNewFormData = []) + { + if (is_array($aNewFormData) && count($aNewFormData)) { + $this->setFormarray($aNewFormData); } - return true; } /** - * set a new array + * Set a new array for a new form * @param array $aNewFormData * @return boolean */ - public function setFormarray($aNewFormData = array()) { + public function setFormarray($aNewFormData = []) + { if (!is_array($aNewFormData) || !count($aNewFormData)) { return false; } - return $this->aForm = $aNewFormData; + $this->aForm = $aNewFormData; + return true; } /** - * get html code for a completely rendered form - * @param string $sFormId + * Get html code for a completely rendered form + * @param string $sFormId * @return string html output */ - public function renderHtml($sFormId) { + public function renderHtml(string $sFormId): string + { $sReturn = false; if (!isset($this->aForm[$sFormId])) { - die("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - form id " . $sFormId . " does not exist."); + throw new Exception("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - form id " . $sFormId . " does not exist."); } // FORM tag - $sReturn.='<form '; + $sReturn .= '<form '; if (isset($this->aForm[$sFormId]["meta"])) { - foreach (array("method", "action", "target", "accept-charset", "class", "id", "name") as $sAttr) { + foreach (["method", "action", "target", "accept-charset", "class", "id", "name"] as $sAttr) { if (isset($this->aForm[$sFormId]["meta"][$sAttr])) { - $sReturn.=$sAttr . '="' . $this->aForm[$sFormId]["meta"][$sAttr] . '" '; + $sReturn .= $sAttr . '="' . $this->aForm[$sFormId]["meta"][$sAttr] . '" '; } } } - $sReturn.='>'; - + $sReturn .= '>'; + // ... and all its elements foreach ($this->aForm[$sFormId]["form"] as $elementKey => $elementData) { - $sReturn.=$this->renderHtmlElement($elementKey, $elementData); + $sReturn .= $this->renderHtmlElement($elementKey, $elementData); } - $sReturn.='</form>'; + $sReturn .= '</form>'; return $sReturn; } /** - * add html attributes if they exist + * Add html attributes if they exist * @param array $aAttributes list of attributes to search for * @param array $elementData array of form element * @return string */ - private function _addHtmlAtrributes($aAttributes, $elementData) { + private function _addHtmlAtrributes(array $aAttributes, array $elementData): string + { $sReturn = false; foreach ($aAttributes as $sAtrr) { if (isset($elementData[$sAtrr]) && $elementData[$sAtrr]) { - $sReturn.=($sReturn ? ' ' : '') - .$sAtrr . '="' . $elementData[$sAtrr] . '"' + $sReturn .= ($sReturn ? ' ' : '') + . $sAtrr . '="' . $elementData[$sAtrr] . '"' ; } } return $sReturn; } - private function _addLabel($sLabel, $sFor, $sClass = false) { - $sReturn = false; - $sReturn = '<label for="' . $sFor . '"'; - if ($sClass) - $sReturn.=' class="' . $sClass . '"'; - $sReturn.='>' . $sLabel . '</label>'; - $sReturn.="\n"; - return $sReturn; + /** + * Add a label next to a form element + * @param string $sLabel Labeltext to show + * @param string $sFor for attribute to ad (points to the id of the form element) + * @param string $sClass css class + * @return string + */ + private function _addLabel(string $sLabel, string $sFor, string $sClass = ''): string + { + return "<label for=\"$sFor\"" + . ($sClass ? " class=\"$sClass\"" : '') + . ">$sLabel</label>" + . "\n" + ; } - private function _checkReqiredKeys($aArray, $aRequiredKeys, $sLabel = false) { + /** + * Ensure that all required keys are set + * @param array $aArray given array + * @param array $aRequiredKeys set of required keys + * @param string $sLabel form label - will be shown in error message + * @throws \Exception + * @return bool + */ + private function _checkReqiredKeys(array $aArray, array $aRequiredKeys, string $sLabel = ''): bool + { $bReturn = true; foreach ($aRequiredKeys as $sKey) { if (!isset($aArray[$sKey])) { - die("ERROR: $sLabel<br>Missing key \"$sKey\" in the array of a form element:<pre>" . print_r($aArray, true) . "</pre>"); - $bReturn = false; + throw new Exception("ERROR: $sLabel<br>Missing key \"$sKey\" in the array of a form element:<pre>" . print_r($aArray, true) . "</pre>"); } } return $bReturn; } /** - * render a single form element + * Render a single form element * @param string $sId id of a form element * @param array $elementData array of form element * @return string html output */ - public function renderHtmlElement($sId, $elementData) { + public function renderHtmlElement(string $sId, array $elementData): string + { $sReturn = false; - $aAllowedHtmlAttributes = array(); $sDefaultAttributes = "" - . "class," - - // events ... see https://developer.mozilla.org/en-US/docs/Web/API/Element - . "onauxclick,onclick,ondblclick,oncontextmenu,onfocusin,onfocusout," - . "onkeydown,onkeypress,onkeyup," - . "onmousedown,onmouseenter,onmouseleave,onmousemove,onmouseout,onmouseover,onmouseup," - // - . "title" - ; + . "class," + + // events ... see https://developer.mozilla.org/en-US/docs/Web/API/Element + . "onauxclick,onclick,ondblclick,oncontextmenu,onfocusin,onfocusout," + . "onkeydown,onkeypress,onkeyup," + . "onmousedown,onmouseenter,onmouseleave,onmousemove,onmouseout,onmouseover,onmouseup," + // + . "title" + ; if (!isset($elementData["type"])) { print_r($elementData); - die("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - key "type" does not exist."); + throw new Exception("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - key "type" does not exist."); } $sFormElement = false; @@ -144,36 +164,36 @@ class formgen { if (isset($elementData["label"])) { $sLabelText = $elementData["label"]; - $sLabelText.=(isset($elementData["required"]) && $elementData["required"]) ? $this->sRequired : ''; + $sLabelText .= (isset($elementData["required"]) && $elementData["required"]) ? $this->sRequired : ''; } switch ($elementData["type"]) { case "button": - $this->_checkReqiredKeys($elementData, array("value")); - $elementData["class"]=$elementData["class"] ? $elementData["class"] : "btn btn-default"; - $sFormElement.=' <button id="' . $sId . '" '; - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,name"), $elementData); - $sFormElement.='>' . $elementData["value"] . '</button>'; - $sFormElement.="\n"; + $this->_checkReqiredKeys($elementData, ["value"]); + $elementData["class"] = $elementData["class"] ? $elementData["class"] : "btn btn-default"; + $sFormElement .= ' <button id="' . $sId . '" '; + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,name"), $elementData); + $sFormElement .= '>' . $elementData["value"] . '</button>'; + $sFormElement .= "\n"; $sHtmlDefault = $sFormElement; break; case "checkbox": - $this->_checkReqiredKeys($elementData, array("name")); + $this->_checkReqiredKeys($elementData, ["name"]); foreach ($elementData["options"] as $idOption => $aOptionData) { - $sFormElement.="\n".'<div class="checkbox">'; + $sFormElement .= "\n" . '<div class="checkbox">'; $s = preg_replace('/\W/iu', '', $sId . $idOption); $sOptionId = preg_replace('/[äöüß]/i', '', $s); - $sFormElement.=' <input type="checkbox" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked"), $aOptionData); - $sFormElement.=' name="' . $elementData["name"] . '[]"'; - $sFormElement.='/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>'; + $sFormElement .= ' <input type="checkbox" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked"), $aOptionData); + $sFormElement .= ' name="' . $elementData["name"] . '[]"'; + $sFormElement .= '/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>'; } - $sFormElement.="\n"; + $sFormElement .= "\n"; // $sLabelElement.='<span class="help-block">' . $sLabelText . '</span>'; - $sLabelElement.='<div class="col-sm-2">' . $sLabelText . '</div>'; - $sLabelElement.="\n"; + $sLabelElement .= '<div class="col-sm-2">' . $sLabelText . '</div>'; + $sLabelElement .= "\n"; // $sHtmlDefault = $sLabelElement . $sFormElement; $sHtmlDefault = $sLabelElement . '<div class="col-sm-10">' . "\n" . $sFormElement . '</div>' . "\n"; @@ -181,11 +201,11 @@ class formgen { break; case "hidden": - $this->_checkReqiredKeys($elementData, array("value")); - $sFormElement.=' <input type="hidden" id="' . $sId . '" '; - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "name,value"), $elementData); - $sFormElement.=" />"; - $sFormElement.="\n"; + $this->_checkReqiredKeys($elementData, ["value"]); + $sFormElement .= ' <input type="hidden" id="' . $sId . '" '; + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "name,value"), $elementData); + $sFormElement .= " />"; + $sFormElement .= "\n"; $sHtmlDefault = $sFormElement . "\n"; break; @@ -196,17 +216,17 @@ class formgen { break; case "radio": - $this->_checkReqiredKeys($elementData, array("name")); + $this->_checkReqiredKeys($elementData, ["name"]); foreach ($elementData["options"] as $idOption => $aOptionData) { - $sFormElement.="\n".'<div class="radio">'; + $sFormElement .= "\n" . '<div class="radio">'; $s = preg_replace('/\W/iu', '', $sId . $idOption); $sOptionId = preg_replace('/[äöüß]/i', '', $s); - $sFormElement.=' <input type="radio" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,disabled"), $aOptionData); - $sFormElement.=" " . $this->_addHtmlAtrributes(explode(",", "name"), $elementData); - $sFormElement.='/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>'; + $sFormElement .= ' <input type="radio" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,disabled"), $aOptionData); + $sFormElement .= " " . $this->_addHtmlAtrributes(explode(",", "name"), $elementData); + $sFormElement .= '/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>'; } - $sFormElement.="\n"; + $sFormElement .= "\n"; if ($sLabelText) { // $sLabelElement.='<span class="help-block">' . $sLabelText . '</span>' . "\n"; @@ -224,24 +244,24 @@ class formgen { case "select": // HINWEIS optgroups werden nicht unterstuezt - nur einfache Listen - $this->_checkReqiredKeys($elementData, array("name")); - $sDivClass=(isset($elementData["inline"]) && $elementData["inline"])?"form-group":"col-sm-10"; - $elementData['class'].=" form-control"; - $sFormElement.='<div class="'.$sDivClass.'">'."\n".'<select id="' . $sId . '" '; - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,onchange"), $elementData); - $sFormElement.=">\n"; + $this->_checkReqiredKeys($elementData, ["name"]); + $sDivClass = (isset($elementData["inline"]) && $elementData["inline"]) ? "form-group" : "col-sm-10"; + $elementData['class'] .= " form-control"; + $sFormElement .= '<div class="' . $sDivClass . '">' . "\n" . '<select id="' . $sId . '" '; + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,onchange"), $elementData); + $sFormElement .= ">\n"; foreach ($elementData["options"] as $idOption => $aOptionData) { $s = preg_replace('/\W/iu', '', $sId . $idOption); $sOptionId = preg_replace('/[äöüß]/i', '', $s); - $sFormElement.=' <option value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,selected"), $aOptionData); - $sFormElement.='>' . $aOptionData["label"] . '</option>' . "\n"; + $sFormElement .= ' <option value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,selected"), $aOptionData); + $sFormElement .= '>' . $aOptionData["label"] . '</option>' . "\n"; } - $sFormElement.="</select></div>\n"; + $sFormElement .= "</select></div>\n"; if ($sLabelText) { // $sLabelElement.='<span class="help-block">' . $sLabelText . '</span>' . "\n"; - $sLabelClass=(isset($elementData["inline"]) && $elementData["inline"])?"":"col-sm-2"; + $sLabelClass = (isset($elementData["inline"]) && $elementData["inline"]) ? "" : "col-sm-2"; $sLabelElement = $this->_addLabel($sLabelText, $sId, $sLabelClass); } @@ -252,40 +272,40 @@ class formgen { break; case "submit": - $this->_checkReqiredKeys($elementData, array("value")); - $sClass="btn btn-primary "; - if (isset($elementData["class"])){ - $sClass.=$elementData["class"]; + $this->_checkReqiredKeys($elementData, ["value"]); + $sClass = "btn btn-primary "; + if (isset($elementData["class"])) { + $sClass .= $elementData["class"]; } - $elementData["class"]=$sClass; - $sFormElement.=' <button id="' . $sId . '" type="submit" '; - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes"), $elementData); - $sFormElement.='>' . $elementData["value"] . '</button>'; - $sFormElement.="\n"; + $elementData["class"] = $sClass; + $sFormElement .= ' <button id="' . $sId . '" type="submit" '; + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes"), $elementData); + $sFormElement .= '>' . $elementData["value"] . '</button>'; + $sFormElement .= "\n"; $sHtmlDefault = $sFormElement; break; case "text": case "password": - $this->_checkReqiredKeys($elementData, array("name")); - $sFormElement.=' <input type="'.$elementData["type"].'" id="' . $sId . '" class="form-control col-sm-10" '; + $this->_checkReqiredKeys($elementData, ["name"]); + $sFormElement .= ' <input type="' . $elementData["type"] . '" id="' . $sId . '" class="form-control col-sm-10" '; $aAllowedHtmlAttributes["text"] = explode(",", ""); - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,autocomplete,autofocus,list,disabled,onchange,pattern,placeholder,required,size,value"), $elementData); - // $sFormElement.=$this->_addHtmlAtrributes(array("name", "value", "size", "placeholder", "required"), $elementData); + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,autocomplete,autofocus,list,disabled,onchange,pattern,placeholder,required,size,value"), $elementData); + // $sFormElement.=$this->_addHtmlAtrributes(["name", "value", "size", "placeholder", "required"], $elementData); // IE: Return abfangen lassen // $sFormElement.=' onkeypress="return checkKey(event);"'; - $sFormElement.=' />'; - $sFormElement.="\n"; + $sFormElement .= ' />'; + $sFormElement .= "\n"; - if (isset($elementData["class"]) && $elementData["inline"]){ + if (isset($elementData["class"]) && $elementData["inline"]) { $sLabelElement = $this->_addLabel($sLabelText, $sId, "col-sm-2"); // $sHtmlDefault = $sLabelElement . "\n" . $sFormElement . "\n"; - $sHtmlDefault = $sLabelElement . '<div class="col-sm-3">' . "\n" . $sFormElement . '</div>' . "\n"; + $sHtmlDefault = $sLabelElement . '<div class="col-sm-3">' . "\n" . $sFormElement . '</div>' . "\n"; } else { $sLabelElement = $this->_addLabel($sLabelText, $sId, "col-sm-2"); // $sHtmlDefault = $sLabelElement . '<div class="controls">' . "\n" . $sFormElement . '</div>' . "\n"; - $sHtmlDefault = $sLabelElement . '<div class="col-sm-10">' . "\n" . $sFormElement . '</div>' . "\n"; + $sHtmlDefault = $sLabelElement . '<div class="col-sm-10">' . "\n" . $sFormElement . '</div>' . "\n"; } $sHtmlTable = '<td>' . $sLabelText . '</td><td>' . $sFormElement . '</td>'; @@ -293,13 +313,13 @@ class formgen { break; case "textarea": - $this->_checkReqiredKeys($elementData, array("name")); - $sFormElement.=' <textarea id="' . $sId . '" class="form-control col-sm-10" '; + $this->_checkReqiredKeys($elementData, ["name"]); + $sFormElement .= ' <textarea id="' . $sId . '" class="form-control col-sm-10" '; $aAllowedHtmlAttributes["text"] = explode(",", ""); - $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,onchange,placeholder,required,cols,rows"), $elementData); - // $sFormElement.=$this->_addHtmlAtrributes(array("name", "value", "size", "placeholder", "required"), $elementData); - $sFormElement.='>'.$elementData['value'].'</textarea>'; - $sFormElement.="\n"; + $sFormElement .= $this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,onchange,placeholder,required,cols,rows"), $elementData); + // $sFormElement.=$this->_addHtmlAtrributes(["name", "value", "size", "placeholder", "required"], $elementData); + $sFormElement .= '>' . $elementData['value'] . '</textarea>'; + $sFormElement .= "\n"; $sLabelElement = $this->_addLabel($sLabelText, $sId, "control-label col-sm-2"); @@ -311,31 +331,29 @@ class formgen { default: die("ERROR: " . __CLASS__ . ":" . __FUNCTION__ . " - formelement type " . $elementData["type"] . " ist not supported (yet)."); - break; } // Default or table mode? if (isset($elementData["mode"]) && $elementData["mode"] == 'table' && $sHtmlTable) { $sHtmlDefault = $sHtmlTable; } else { - if ($elementData["type"] != "button" - && $elementData["type"] != "fieldset" + if ( + $elementData["type"] != "button" + && $elementData["type"] != "fieldset" && $elementData["type"] != "markup" && $elementData["type"] != "hidden" ) { - if (!isset($elementData["inline"]) || !$elementData["inline"]){ + if (!isset($elementData["inline"]) || !$elementData["inline"]) { // $sHtmlDefault = "<fieldset>" . $sHtmlDefault . "</fieldset>\n"; - $sHtmlDefault = '<div class="form-group row">' . $sHtmlDefault . '</div>'."\n"; + $sHtmlDefault = '<div class="form-group row">' . $sHtmlDefault . '</div>' . "\n"; } } } - $sReturn.="<!-- " . $elementData["type"] . " -->\n"; - $sReturn.=$sHtmlDefault . "\n"; + $sReturn .= "<!-- " . $elementData["type"] . " -->\n"; + $sReturn .= $sHtmlDefault . "\n"; return $sReturn; } } - -?> \ No newline at end of file diff --git a/public_html/deployment/classes/htmlelements.class.php b/public_html/deployment/classes/htmlelements.class.php index 0296b1783d125690e684151780a94bbe082b9499..b890fda4dd77302de87fa59e603fb353ce763543 100755 --- a/public_html/deployment/classes/htmlelements.class.php +++ b/public_html/deployment/classes/htmlelements.class.php @@ -15,27 +15,33 @@ * - icon - will be added as <i class="[icon value]"></i> to the label * * @author Axel + * + * 2024-07-04 <axel.hahn@unibe.ch> added type declarations; update php docs + * 2024-08-26 <axel.hahn@unibe.ch> remove unneeded methods; simplify icon methods; update phpdocs */ -class htmlelements { +class htmlelements +{ /** - * set of auto generated icon prefixes - * @var type + * Extracted label from array with attributes + * @var string */ - var $_aIcons=array( - // 'fa-'=>'fa ', - ); - var $_sLabel = ''; - var $_aAttributes = array(); - + + /** + * Array of attributes for a html tag + * @var array + */ + var $_aAttributes = []; + // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- - - public function __construct() { - return true; + + public function __construct() + { + // nothiung here } // ---------------------------------------------------------------------- @@ -43,27 +49,28 @@ class htmlelements { // PRIVATE FUNCTIONS // // ---------------------------------------------------------------------- - - + + /** * generate html attibutes with all internal attributes key -> values + * to be added in opening tag * @return string */ - protected function _addAttributes() { + protected function _addAttributes(): string + { $sReturn = ''; foreach ($this->_aAttributes as $sAttr => $sValue) { - if(is_array($sValue)){ - echo "ERROR: an html tag was defined with array in attribute [$sAttr]:<br><pre>".print_r($this->_aAttributes, 1)."</pre>"; + if (is_array($sValue)) { + echo "ERROR: an html tag was defined with array in attribute [$sAttr]:<br><pre>" . print_r($this->_aAttributes, 1) . "</pre>"; } - $sReturn .= ' '.$sAttr . '="' . $sValue . '"'; - + $sReturn .= " $sAttr=\"$sValue\""; } return $sReturn; } - - + + /** - * internal helper: fetch all attributes from key-value hash; + * Internal helper: fetch all attributes from key-value hash; * Specialties here: * - label will be extracted from key 'label' * - and optional existing key 'icon' will be added at beginning of a label @@ -71,17 +78,18 @@ class htmlelements { * @param array $aAttributes * @return boolean */ - protected function _setAttributes($aAttributes){ - $this->_sLabel=''; - if(isset($aAttributes['icon']) && $aAttributes['icon']){ - $this->_sLabel.=$this->getIcon($aAttributes['icon']); + protected function _setAttributes(array $aAttributes): bool + { + $this->_sLabel = ''; + if (isset($aAttributes['icon']) && $aAttributes['icon']) { + $this->_sLabel .= $this->getIcon($aAttributes['icon']); unset($aAttributes['icon']); } - if(isset($aAttributes['label']) && $aAttributes['label']){ + if (isset($aAttributes['label']) && $aAttributes['label']) { $this->_sLabel .= $aAttributes['label']; unset($aAttributes['label']); } - $this->_aAttributes=$aAttributes; + $this->_aAttributes = $aAttributes; return true; } @@ -91,21 +99,22 @@ class htmlelements { // HTML GENERIC // // ---------------------------------------------------------------------- - + /** - * generic function to get html code for a single tag + * Generic function to get html code for a single tag * * @param string $sTag tag name * @param array $aAttributes array with attributes (optional including 'icon' and 'label') * @param boolean $bCloseTag optional: set false if tag has no closing tag (= ending with "/>") - * @return type + * @return string html code */ - public function getTag($sTag, $aAttributes, $bCloseTag=true){ + public function getTag(string $sTag, array $aAttributes, bool $bCloseTag = true): string + { $sTpl = $bCloseTag ? "<$sTag%s>%s</$sTag>" : "<$sTag %s/>%s"; $this->_setAttributes($aAttributes); return sprintf($sTpl, $this->_addAttributes(), $this->_sLabel); } - + // ---------------------------------------------------------------------- // // PUBLIC FUNCTIONS @@ -114,109 +123,22 @@ class htmlelements { // ---------------------------------------------------------------------- /** - * helper detect prefix of a string add prefix of a framework + * Helper detect prefix of a string add prefix of a framework * i.e. value "fa-close" detects font awesome and adds "fa " as prefix * * @param string $sIconclass - * @return boolean + * @return string HTML code */ - public function getIcon($sIconclass=false){ - if(!$sIconclass){ + public function getIcon(string $sIconclass = ''): string + { + if (!$sIconclass) { return ''; } - $sPrefix=''; - foreach ($this->_aIcons as $sPrefix =>$add) { - if (strpos($sIconclass, $sPrefix)===0){ - $sPrefix=$add; - continue; - } - } - // do not use this .. it overrides internal attribute vars - // return $this->getTag('i', array('class'=>$sPrefix.$sIconclass)); - return '<i class="'.$sPrefix.$sIconclass.'"></i> '; - } - - - // ---------------------------------------------------------------------- - // - // PUBLIC FUNCTIONS - // HTML COMPONENTS - // - // ---------------------------------------------------------------------- - /** - * get html code for an input field - * - * @param array $aAttributes attributes of the select tag - * @return string - */ - public function getFormInput($aAttributes){ - $sTpl = '<input %s/>'; - $this->_setAttributes($aAttributes); - return sprintf($sTpl, $this->_addAttributes()); - } - /** - * get html code for an option field in a select drop down - * - * @param array $aAttributes attributes of the option tag - * @return string - */ - public function getFormOption($aAttributes){ - $sTpl = '<option %s>%s</option>'; - $this->_setAttributes($aAttributes); - return sprintf($sTpl, $this->_addAttributes(), $this->_sLabel); - } - /** - * get html code for a select drop down - * - * @param array $aAttributes attributes of the select tag - * @param array $aOptions array for all option fields - * @return string - */ - public function getFormSelect($aAttributes, $aOptions=array()){ - // $sTpl = '<select %s>%s</select>'; + // do not use this .. it overrides internal attribute vars + // return $this->getTag('i', ['class'=>$sIconclass]); - if(!count($aOptions)){ - return false; - } - $sOptions=''; - foreach($aOptions as $aOptionAttributes){ - // $sOptions.=$this->getFormOption($aOptionAttributes); - $sOptions.=$this->getTag('option', $aOptionAttributes); - } - $aAttributes['label']=$sOptions; - return $this->getTag('select', $aAttributes); - /* - $this->_setAttributes($aAttributes); - return sprintf($sTpl, $this->_addAttributes(), $sOptions); - * - */ + return "<i class=\"$sIconclass\"></i> "; } - public function getTable($aHead, $aBody, $aTableAttributes=array()){ - $sReturn=''; - $sTdata=''; - $sThead=''; - $sTpl = '<table %s>' - . '<thead><tr>%s</tr></thead>' - . '<tbody>%s</tbody>' - . '</table>'; - - foreach($aHead as $sTh){ - $sThead.='<th>'.$sTh.'</th>'; - } - foreach($aBody as $aTr){ - $sTdata.='<tr>'; - foreach($aTr as $sTd){ - $sTdata.='<td>'.$sTd.'</td>'; - } - $sTdata.='</tr>'; - } - $this->_setAttributes($aTableAttributes); - return sprintf($sTpl, - $this->_addAttributes(), - $sThead, - $sTdata - ); - } } diff --git a/public_html/deployment/classes/htmlguielements.class.php b/public_html/deployment/classes/htmlguielements.class.php index dd40f33658a98036f824c4f5027e4e2c909ba583..5e86bb78f059a8c2867c4a6cc81d16ab59744325 100644 --- a/public_html/deployment/classes/htmlguielements.class.php +++ b/public_html/deployment/classes/htmlguielements.class.php @@ -1,7 +1,8 @@ <?php /** * html gui elements - * for bootstrap 3 + * for bootstrap 3..5 + * * CI SERVER GUI * * $oHtml=new htmlguielements(); @@ -9,305 +10,236 @@ * echo $oHtml->getBox('error', 'errormessage'); * echo $oHtml->getIcon('fa-pencil'); * - * echo $oHtml->getLink(array( + * echo $oHtml->getLink([ * 'href'=>'https://www.axel-hahn.de', * 'class'=>'btn btn-primary', * 'icon'=>'fa-close', * 'label'=>'linked text', - * )); + * ]); * * echo $oHtml->getTabs( - * array( + * [ * 'tab 1'=>'Inhalt #1', * 'tab 2'=>'Inhalt #2', - * ) + * ] * ); * * * @author hahn + * + * 2024-08-23 v1.1 Axel Hahn php8 only; added variable types; short array syntax; remove unneeded methods */ -class htmlguielements{ - - var $aCfg=array( - /* - 'buttonsOLD'=>array( - // bootstrap defaults - 'primary'=>array('class'=>'btn-primary', 'icon'=>''), - 'success'=>array('class'=>'btn-success', 'icon'=>''), - 'info'=>array('class'=>'btn-info', 'icon'=>''), - 'warning'=>array('class'=>'btn-warning', 'icon'=>''), - 'danger'=>array('class'=>'btn-danger', 'icon'=>''), +class htmlguielements +{ - // custom buttons - 'close'=>array('class'=>'btn-danger', 'icon'=>'fa-close'), - 'error'=>array('class'=>'btn-danger', 'icon'=>'fa-bolt'), - 'ok'=>array('class'=>'btn-primary', 'icon'=>'fa-check'), - - // deploy actions and buttons - 'accept'=>array('class'=>''), - 'build'=>array('class'=>''), - 'cleanup'=>array('class'=>''), - 'deploy'=>array('class'=>'', 'icon'=>'glyphicon-forward'), - 'new'=>array('class'=>'', 'icon'=>'glyphicon-star-empty'), - 'overview'=>array('class'=>''), - 'phase'=>array('class'=>'', 'icon'=>'glyphicon-chevron-right'), - 'rollback'=>array('class'=>'', 'icon'=>'glyphicon-forward'), - 'setup'=>array('class'=>''), - - ), - 'iconsOLD'=>array( - - 'menu'=>'fa-chevron-right', - 'overview'=>'fa-list', - 'project'=>'fa-book', - 'project-home'=>'fa-home', - 'projects'=>'fa-folder-o', - 'actions'=>'fa-check', - - 'actionlog'=>'fa-list-ul', - 'accept'=>'glyphicon-forward', - 'build'=>'glyphicon-equalizer', - 'cleanup'=>'fa-trash', - 'checklang'=>'fa-check', - 'delete'=>'fa-close', - 'deploy'=>'glyphicon-forward', - 'filter'=>'glyphicon-filter', - 'new'=>'glyphicon-star-empty', - 'phase'=>'glyphicon-chevron-right', - 'rollback'=>'glyphicon-forward', - 'setup'=>'fa-cog', - 'login'=>'fa-lock', - 'user'=>'fa-user', - - 'workflow'=>'fa-angle-double-right', - 'repository'=>'fa-database', - 'phase'=>'fa-flag', - 'package'=>'fa-cubes', - 'version'=>'fa-tag', - 'list'=>'fa-list', - 'raw-data'=>'fa-file-o', - - 'back'=>'fa-chevron-left', - - 'branch'=>'glyphicon-bookmark', - 'calendar'=>'glyphicon-calendar', - 'comment'=>'glyphicon-comment', - 'revision'=>'glyphicon-tag', - - 'link-extern'=>'glyphicon-globe', - - 'host'=>'fa-hdd-o', - 'hostgroup'=>'fa-sitemap', - 'templatefile'=>'fa-file-code-o', - 'targetfile'=>'fa-file-o', - 'replace'=>'fa-random', - - 'sign-info'=>'', - 'sign-warning'=>'', - 'sign-error'=>'fa-bolt', - 'sign-ok'=>'', - ), - */ - 'buttons'=>array( + /** + * Configuration array with icons + * @var array + */ + var $aCfg = [ + 'buttons' => [ // bootstrap defaults - 'primary'=>array('class'=>'btn-primary', 'icon'=>''), - 'success'=>array('class'=>'btn-success', 'icon'=>''), - 'info'=>array('class'=>'btn-info', 'icon'=>''), - 'warning'=>array('class'=>'btn-warning', 'icon'=>''), - 'danger'=>array('class'=>'btn-danger', 'icon'=>''), + 'primary' => ['class' => 'btn-primary', 'icon' => ''], + 'success' => ['class' => 'btn-success', 'icon' => ''], + 'info' => ['class' => 'btn-info', 'icon' => ''], + 'warning' => ['class' => 'btn-warning', 'icon' => ''], + 'danger' => ['class' => 'btn-danger', 'icon' => ''], // custom buttons - 'close'=>array('class'=>'btn-danger', 'icon'=>'fa-solid fa-times'), - 'error'=>array('class'=>'btn-danger', 'icon'=>'fa-solid fa-bolt'), - 'ok'=>array('class'=>'btn-primary', 'icon'=>'fa-solid fa-check'), - + 'close' => ['class' => 'btn-danger', 'icon' => 'fa-solid fa-times'], + 'error' => ['class' => 'btn-danger', 'icon' => 'fa-solid fa-bolt'], + 'ok' => ['class' => 'btn-primary', 'icon' => 'fa-solid fa-check'], + // deploy actions and buttons - 'accept'=>array('class'=>''), - 'build'=>array('class'=>''), - 'cleanup'=>array('class'=>''), - 'deploy'=>array('class'=>'', 'icon'=>'fa-solid fa-forward'), - 'new'=>array('class'=>'', 'icon'=>'fa-regular fa-star'), - 'overview'=>array('class'=>''), - 'phase'=>array('class'=>'', 'icon'=>'fa-solid fa-chevron-right'), - 'rollback'=>array('class'=>'', 'icon'=>'fa-solid fa-forward'), - 'setup'=>array('class'=>''), - - ), - 'icons'=>array( - - 'menu'=>'fa-solid fa-chevron-right', - 'valuestore'=>'fa-solid fa-tags', - 'overview'=>'fa-solid fa-list', - 'project'=>'fa-solid fa-book', - 'project-home'=>'fa-solid fa-home', - 'projects'=>'fa-regular fa-folder', - 'actions'=>'fa-solid fa-check', - - 'actionlog'=>'fa-solid fa-list-ul', + 'accept' => ['class' => ''], + 'build' => ['class' => ''], + 'cleanup' => ['class' => ''], + 'deploy' => ['class' => '', 'icon' => 'fa-solid fa-forward'], + 'new' => ['class' => '', 'icon' => 'fa-regular fa-star'], + 'overview' => ['class' => ''], + 'phase' => ['class' => '', 'icon' => 'fa-solid fa-chevron-right'], + 'rollback' => ['class' => '', 'icon' => 'fa-solid fa-forward'], + 'setup' => ['class' => ''], + + ], + 'icons' => [ + + 'menu' => 'fa-solid fa-chevron-right', + 'valuestore' => 'fa-solid fa-tags', + 'overview' => 'fa-solid fa-list', + 'project' => 'fa-solid fa-book', + 'project-home' => 'fa-solid fa-home', + 'projects' => 'fa-regular fa-folder', + 'actions' => 'fa-solid fa-check', + + 'actionlog' => 'fa-solid fa-list-ul', // 'accept'=>'fa-solid fa-forward', - 'accept'=>'fa-solid fa-check', - 'build'=>'fa-solid fa-box-open', - 'checklang'=>'fa-solid fa-check', - 'cleanup'=>'fa-solid fa-broom', - 'close'=>'fa-solid fa-times', - 'delete'=>'fa-solid fa-trash', - 'deploy'=>'fa-solid fa-forward', - 'deploy-configfile'=>'fa-regular fa-file-code', - 'deploy-rollout-plugin'=>'fa-solid fa-plug', - 'filter'=>'fa-solid fa-filter', - 'foreman'=>'fa-solid fa-hard-hat', - 'gotop'=>'fa-solid fa-arrow-up', - 'help'=>'fa-solid fa-life-ring', - 'login'=>'fa-solid fa-right-to-bracket', - 'new'=>'fa-regular fa-star', - 'phase'=>'fa-solid fa-chevron-right', - 'poweroff'=>'fa-solid fa-power-off', - 'refresh'=>'fa-solid fa-sync', - 'rollback'=>'fa-solid fa-forward', - 'setup'=>'fa-solid fa-cog', - 'time'=>'fa-regular fa-clock', - 'waiting'=>'fa-solid fa-clock', - 'user'=>'fa-solid fa-user', - 'user-profile'=>'fa-regular fa-id-card', - 'user-group'=>'fa-regular fa-bookmark', - 'user-permission'=>'fa-solid fa-caret-right', - - 'workflow'=>'fa-solid fa-angle-double-right', - 'repository'=>'fa-solid fa-database', - 'phase'=>'fa-solid fa-flag', - 'package'=>'fa-solid fa-cubes', - 'version'=>'fa-solid fa-tag', - 'list'=>'fa-solid fa-list', - 'raw-data'=>'fa-regular fa-file', - 'method'=>'fa-solid fa-cogs', - 'url'=>'fa-solid fa-globe-americas', - - 'back'=>'fa-solid fa-chevron-left', - - 'branch'=>'fa-solid fa-bookmark', - 'calendar'=>'fa-regular fa-calendar', - 'comment'=>'fa-regular fa-comment', - 'revision'=>'fa-solid fa-tag', - - 'link-extern'=>'fa-solid fa-globe-americas', - - 'host'=>'fa-regular fa-hdd', - 'hostgroup'=>'fa-solid fa-sitemap', - 'file-any'=>'fa-regular fa-file', - 'file-archive'=>'fa-regular fa-file-archive', - 'file-code'=>'fa-regular fa-file-code', - 'file-meta'=>'fa-regular fa-file', - 'file-template'=>'fa-regular fa-file-alt', - 'file-target'=>'fa-solid fa-file-upload', - 'replace'=>'fa-solid fa-random', - - 'box-up'=>'fa-chevron-up', - 'box-down'=>'fa-chevron-down', - - 'sign-info'=>'fa-solid fa-info', - 'sign-warning'=>'fa-solid fa-exclamation', - 'sign-error'=>'fa-solid fa-bolt', - 'sign-ok'=>'fa-solid fa-check', - 'sign-success'=>'fa-solid fa-check', - ), - ); - - public function __construct() { - return true; + 'accept' => 'fa-solid fa-check', + 'build' => 'fa-solid fa-box-open', + 'checklang' => 'fa-solid fa-check', + 'cleanup' => 'fa-solid fa-broom', + 'close' => 'fa-solid fa-times', + 'delete' => 'fa-solid fa-trash', + 'deploy' => 'fa-solid fa-forward', + 'deploy-configfile' => 'fa-regular fa-file-code', + 'deploy-rollout-plugin' => 'fa-solid fa-plug', + 'filter' => 'fa-solid fa-filter', + 'foreman' => 'fa-solid fa-hard-hat', + 'gotop' => 'fa-solid fa-arrow-up', + 'help' => 'fa-solid fa-life-ring', + 'login' => 'fa-solid fa-right-to-bracket', + 'new' => 'fa-regular fa-star', + // 'phase' => 'fa-solid fa-chevron-right', + 'poweroff' => 'fa-solid fa-power-off', + 'refresh' => 'fa-solid fa-sync', + 'rollback' => 'fa-solid fa-forward', + 'setup' => 'fa-solid fa-cog', + 'time' => 'fa-regular fa-clock', + 'waiting' => 'fa-solid fa-clock', + 'user' => 'fa-solid fa-user', + 'user-profile' => 'fa-regular fa-id-card', + 'user-group' => 'fa-regular fa-bookmark', + 'user-permission' => 'fa-solid fa-caret-right', + + 'workflow' => 'fa-solid fa-angle-double-right', + 'repository' => 'fa-solid fa-database', + 'phase' => 'fa-solid fa-flag', + 'package' => 'fa-solid fa-cubes', + 'version' => 'fa-solid fa-tag', + 'list' => 'fa-solid fa-list', + 'raw-data' => 'fa-regular fa-file', + 'method' => 'fa-solid fa-cogs', + 'url' => 'fa-solid fa-globe-americas', + + 'back' => 'fa-solid fa-chevron-left', + + 'branch' => 'fa-solid fa-bookmark', + 'calendar' => 'fa-regular fa-calendar', + 'comment' => 'fa-regular fa-comment', + 'revision' => 'fa-solid fa-tag', + + 'link-extern' => 'fa-solid fa-globe-americas', + + 'host' => 'fa-regular fa-hdd', + 'hostgroup' => 'fa-solid fa-sitemap', + 'file-any' => 'fa-regular fa-file', + 'file-archive' => 'fa-regular fa-file-archive', + 'file-code' => 'fa-regular fa-file-code', + 'file-meta' => 'fa-regular fa-file', + 'file-template' => 'fa-regular fa-file-alt', + 'file-target' => 'fa-solid fa-file-upload', + 'replace' => 'fa-solid fa-random', + + 'box-up' => 'fa-solid fa-chevron-up', + 'box-down' => 'fa-solid fa-chevron-down', + + 'sign-info' => 'fa-solid fa-info', + 'sign-warning' => 'fa-solid fa-exclamation', + 'sign-error' => 'fa-solid fa-bolt', + 'sign-ok' => 'fa-solid fa-check', + 'sign-success' => 'fa-solid fa-check', + ], + ]; + + /** + * Constructor + */ + public function __construct() + { + // nothing here } - + // ---------------------------------------------------------------------- // helper function // ---------------------------------------------------------------------- - + /** - * add an html attribute if the attribute exists as a key + * Add an html attribute if the attribute exists as a key * @param string $sAttribute html attribute to add - * @param string $aData item array + * @param array $aData item array * @param string $sDefault use default if key does not exists * @return string */ - public function addAttributeFromKey($sAttribute, $aData, $sDefault=''){ - return (isset($aData[$sAttribute]) - ? $this->addAttribute($sAttribute, $aData[$sAttribute]) - : $this->addAttribute($sAttribute, $sDefault) - ); + public function addAttributeFromKey(string $sAttribute, array $aData, string $sDefault = ''): string + { + return (isset($aData[$sAttribute]) + ? $this->addAttribute($sAttribute, $aData[$sAttribute]) + : $this->addAttribute($sAttribute, $sDefault) + ); } - + /** - * add an html attribute if value is not empty + * Add an html attribute if value is not empty * @param string $sAttribute html attribute to add * @param string $sValue value of attribute * @return string */ - public function addAttribute($sAttribute, $sValue){ - return ($sValue ? ' '.$sAttribute.'="'.$sValue.'"' : '' ); + public function addAttribute(string $sAttribute, string $sValue): string + { + return ($sValue ? ' ' . $sAttribute . '="' . $sValue . '"' : ''); } - + /** - * get html attributes as string from all keys of given hash + * Get html attributes as string from all keys of given hash * * @param array $aItem * @return string */ - public function addAllAttributes($aItem){ - $sReturn=''; - foreach (array_keys($aItem) as $sKey){ - $sReturn.=$this->addAttributeFromKey($sKey, $aItem); + public function addAllAttributes(array $aItem): string + { + $sReturn = ''; + foreach (array_keys($aItem) as $sKey) { + $sReturn .= $this->addAttributeFromKey($sKey, $aItem); } return $sReturn; } - + // ---------------------------------------------------------------------- // low level // ---------------------------------------------------------------------- - + /** - * get html code for icon; glypphicons and font-awesome is supported + * Get html code for icon; glypphicons and font-awesome is supported * * @param string $sLabel label of icon * @return string */ - public function getIcon($sLabel){ - if(!$sLabel){ + public function getIcon(string $sLabel): string + { + if (!$sLabel) { return ''; } - $sPrefix=( - strpos($sLabel, 'glyphicon-')===0 ? 'glyphicon' - : ( strpos($sLabel, 'fa-')===0 ? 'fa' : '') - ); - // if(!$sPrefix){ - if(isset($this->aCfg['icons'][$sLabel])){ + if (isset($this->aCfg['icons'][$sLabel])) { return $this->getIconByType($sLabel); } - return '<i'.$this->addAttribute('class', ($sPrefix ? $sPrefix . ' ' : '').$sLabel).'></i> '; + return '<i' . $this->addAttribute('class', $sLabel) . '></i> '; } + /** - * get html code for icon; glypphicons and font-awesome is supported + * Get html code for icon * * @param string $sLabel label of icon * @return string */ - public function getIconClass($sLabel){ - if(!$sLabel){ + public function getIconClass(string $sLabel): string + { + if (!$sLabel) { return ''; } - if(isset($this->aCfg['icons'][$sLabel])){ + if (isset($this->aCfg['icons'][$sLabel])) { return $this->aCfg['icons'][$sLabel]; } - $sPrefix=( - strpos($sLabel, 'glyphicon-')===0 ? 'glyphicon' - : ( strpos($sLabel, 'fa-')===0 ? 'fa' : '') - ); - return ($sPrefix ? $sPrefix . ' ' : '').$sLabel; + return $sLabel; } - + /** - * get a default icon from config + * Get a default icon from config + * * @param string $sType icon type - * @return array + * @return string */ - public function getIconByType($sType){ + public function getIconByType(string $sType): string + { return (isset($this->aCfg['icons'][$sType]) ? $this->getIcon($this->aCfg['icons'][$sType]) : '' @@ -315,53 +247,55 @@ class htmlguielements{ } /** - * get html code for icon; glypphicons and font-awesome is supported + * Get html code for icon; glypphicons and font-awesome is supported * * @param array $aItem array with link attributes; href for target; "label" and "icon" * @return string */ - public function getLink($aItem){ - - $sHref=$this->addAttributeFromKey('href', $aItem, '#'); - $sLabel=(isset($aItem['icon']) ? $this->getIcon($aItem['icon']): '') - .(isset($aItem['label']) ? $aItem['label'] : ''); - - foreach(array('href', 'icon', 'label') as $sKey){ - if (isset($aItem[$sKey])){ + public function getLink(array $aItem): string + { + + $sHref = $this->addAttributeFromKey('href', $aItem, '#'); + $sLabel = (isset($aItem['icon']) ? $this->getIcon($aItem['icon']) : '') + . (isset($aItem['label']) ? $aItem['label'] : ''); + + foreach (['href', 'icon', 'label'] as $sKey) { + if (isset($aItem[$sKey])) { unset($aItem[$sKey]); } } - - $sReturn='<a'.$sHref; - $sReturn.=$this->addAllAttributes($aItem); - $sReturn.='>' - .$sLabel - .'</a>'; + + $sReturn = '<a' . $sHref; + $sReturn .= $this->addAllAttributes($aItem); + $sReturn .= '>' + . $sLabel + . '</a>'; return $sReturn; } - + /** - * add default css classes and colors based on $aItem['type'] and the + * Add default css classes and colors based on $aItem['type'] and the * local default settings in $this->aCfg * * @param array $aItem * @return array */ - protected function _getButtonattributesByType($aItem){ - $aReturn=$aItem; - if (isset($this->aCfg['buttons'][$aItem['type']])){ - $sClass=$this->aCfg['buttons'][$aItem['type']]['class']; - $aReturn['class'].=$sClass ? ' '.$sClass : ''; - + protected function _getButtonattributesByType(array $aItem): array + { + $aReturn = $aItem; + if (isset($this->aCfg['buttons'][$aItem['type']])) { + $sClass = $this->aCfg['buttons'][$aItem['type']]['class']; + $aReturn['class'] .= $sClass ? ' ' . $sClass : ''; + // icon priority: // given in param --> icon in button config --> icon in icon config - $aReturn['icon']=$aReturn['icon'] ? $aReturn['icon'] : - ( $this->aCfg['buttons'][$aItem['type']]['icon'] - ? $this->aCfg['buttons'][$aItem['type']]['icon'] - : ( - isset($this->aCfg['icons'][$aItem['type']]) - ? $this->aCfg['icons'][$aItem['type']] - : '' + $aReturn['icon'] = $aReturn['icon'] ? $aReturn['icon'] : + ($this->aCfg['buttons'][$aItem['type']]['icon'] + ? $this->aCfg['buttons'][$aItem['type']]['icon'] + : ( + isset($this->aCfg['icons'][$aItem['type']]) + ? $this->aCfg['icons'][$aItem['type']] + : '' ) ); } @@ -370,28 +304,29 @@ class htmlguielements{ /** - * get html code for icon; glypphicons and font-awesome is supported + * Get html code for a button like link * * @param array $aItem array with link attributes; href for target; "label" and "icon" * @return string */ - public function getLinkButton($aItem){ - foreach(array('class', 'icon') as $sKey){ - if (!isset($aItem[$sKey])){ - $aItem[$sKey]=''; + public function getLinkButton($aItem) + { + foreach (['class', 'icon'] as $sKey) { + if (!isset($aItem[$sKey])) { + $aItem[$sKey] = ''; } } - - if (isset($aItem['type'])){ - $aItem=$this->_getButtonattributesByType($aItem); + + if (isset($aItem['type'])) { + $aItem = $this->_getButtonattributesByType($aItem); unset($aItem['type']); } // if not class "btn" was added: add "btn" // if not class "btn-[something]" was added: add "btn-default" - $sClass=$aItem['class']; - $sClass=(strstr($sClass, 'btn-') ? '' : 'btn-default ').$sClass; - $sClass=(strstr($sClass, 'btn ') ? '' : 'btn ').$sClass; - $aItem['class']=$sClass; + $sClass = $aItem['class']; + $sClass = (strstr($sClass, 'btn-') ? '' : 'btn-default ') . $sClass; + $sClass = (strstr($sClass, 'btn ') ? '' : 'btn ') . $sClass; + $aItem['class'] = $sClass; // $aItem['label'].=' -> '.$sClass; return $this->getLink($aItem); @@ -402,71 +337,26 @@ class htmlguielements{ // ---------------------------------------------------------------------- /** - * get html code of a div around a message + * Get html code of a div around a message + * * @param string $sWarnlevel one of error|success|info|warning to get a colored box * @param string $sMessage message text * @return string */ - public function getBox($sWarnlevel, $sMessage) { - $aCfg = array( - "error" => array("class" => "alert alert-danger", "prefix" => t("error")), - "success" => array("class" => "alert alert-success", "prefix" => t("success")), - "info" => array("class" => "alert alert-info", "prefix" => t("info")), - "warning" => array("class" => "alert alert-warning", "prefix" => t("warning")), - ); + public function getBox(string $sWarnlevel, string $sMessage): string + { + $aCfg = [ + "error" => ["class" => "alert alert-danger", "prefix" => t("error")], + "success" => ["class" => "alert alert-success", "prefix" => t("success")], + "info" => ["class" => "alert alert-info", "prefix" => t("info")], + "warning" => ["class" => "alert alert-warning", "prefix" => t("warning")], + ]; $sClass = ""; if (isset($aCfg[$sWarnlevel])) { $sClass = $aCfg[$sWarnlevel]["class"]; - $sMessage = '<strong>' . $this->getIcon('sign-'.$sWarnlevel).$aCfg[$sWarnlevel]["prefix"] . '</strong> ' . $sMessage; + $sMessage = '<strong>' . $this->getIcon('sign-' . $sWarnlevel) . $aCfg[$sWarnlevel]["prefix"] . '</strong> ' . $sMessage; } - return '<div'.$this->addAttribute('class', $sClass).'>' . $sMessage . '</div>'; - } - - /** - * get html code for tabs with content - * - * @staticvar int $iCounter internal counter for tabs ans content - * @param array $aTabData tab data; key is the tab label; value the content of its tab - * @return string - */ - public function getNav__UNUSED($aTabData){ - $sTabs=''; - $sContent=''; - - static $iCounter=0; - $iTab=0; - - if (!is_array($aTabData) || !count($aTabData)){ - return false; - } - $sNavType=$aTabData['options']['type']; // "tabs" or "pills" - $sNavCss='nav nav-'.$sNavType; - if (isset($aTabData['options']['stacked']) && $aTabData['options']['stacked']){ - $sNavCss.=' nav-stacked'; - } - if (isset($aTabData['options']['justified']) && $aTabData['options']['justified']){ - $sNavCss.=' nav-justified'; - } - $sNavType=$aTabData['options']['justified']; - foreach ($aTabData['tabs'] as $sTabLabel=>$sTabContent){ - $iCounter++; - $iTab++; - $sId="tab-generated-$iCounter"; - $sTabs.= ($iTab==1 ? '<li class="active"' : '<li') - . ' role="presentation">' - . '<a href="#'.$sId.'" data-toggle="tab">' . $sTabLabel . '</a></li>' - ; - $sContent.='<div class="tab-pane' - .($iTab==1 ? ' active' : '') - .'" id="'.$sId.'">' - .$sTabContent - .'</div>' - ; - } - return '<div class="tabbable">' - . '<ul class="'.$sNavCss.'">'. $sTabs.'</ul>' - . '<div class="tab-content">'.$sContent.'</div>' - . '</div>'; + return '<div' . $this->addAttribute('class', $sClass) . '>' . $sMessage . '</div>'; } /** @@ -475,28 +365,29 @@ class htmlguielements{ * @param array $aTabledata array with subkeys "header" and "body" * @return string */ - public function getTable($aTabledata) { - $sTHead=''; - $sTBody=''; - if (isset($aTabledata['body'])){ - foreach ($aTabledata['body'] as $aRow){ - $sTBody.='<tr>'; - foreach ($aRow as $sItem){ - $sTBody.='<td>'.$sItem.'</td>'; + public function getTable(array $aTabledata): string + { + $sTHead = ''; + $sTBody = ''; + if (isset($aTabledata['body'])) { + foreach ($aTabledata['body'] as $aRow) { + $sTBody .= '<tr>'; + foreach ($aRow as $sItem) { + $sTBody .= '<td>' . $sItem . '</td>'; } - $sTBody.='</tr>'; + $sTBody .= '</tr>'; } } - if (isset($aTabledata['header'])){ - foreach ($aTabledata['header'] as $sItem){ - $sTHead.='<th>'.$sItem.'</th>'; + if (isset($aTabledata['header'])) { + foreach ($aTabledata['header'] as $sItem) { + $sTHead .= '<th>' . $sItem . '</th>'; } } return '<table class="table" style="width: auto;">' - .($sTHead ? '<thead>'.$sTHead.'</thead>' : '') - .($sTBody ? '<tbody>'.$sTBody.'</tbody>' : '') - .'</table>' - ; + . ($sTHead ? '<thead>' . $sTHead . '</thead>' : '') + . ($sTBody ? '<tbody>' . $sTBody . '</tbody>' : '') + . '</table>' + ; } - + } diff --git a/public_html/deployment/classes/ldap.class.php b/public_html/deployment/classes/ldap.class.php index 82e4a57c387cfced3423599f0cb00f6763289b5d..a4d4de90f463c67e2322f375f2d9a3333bf56299 100644 --- a/public_html/deployment/classes/ldap.class.php +++ b/public_html/deployment/classes/ldap.class.php @@ -3,53 +3,66 @@ /** * * IML LDAP CONNECTOR - *<pre> - * 2022-02-22 ah added objGet(), sanitizeFilter() <br> - * 2022-08-18 ah mask password (showing 4 chars only) <br> - * 2022-08-22 ah mhash is deprecated <br> - * 2022-08-26 ah fix verifyPassword <br> - * </pre> - * @author axel.hahn@iml.unibe.ch + * + * @author axel.hahn@unibe.ch + * @license GNU GPL v3 + * + * SOURCE: <https://git-repo.iml.unibe.ch/iml-open-source/ldap-php-class/> + * DOCS: <https://os-docs.iml.unibe.ch/ldap-php-class/index.html> + * + * 2022-02-22 ah added objGet(), sanitizeFilter() + * 2022-08-18 ah mask password (showing 4 chars only) + * 2022-08-22 ah mhash is deprecated + * 2022-08-26 ah fix verifyPassword + * 2024-07-11 ah php8 only: use variable types; update phpdocs + * 2024-07-12 ah remove connection port (use server value "ldaps://<host>:<port>" if needed) */ -class imlldap { +class imlldap +{ // ---------------------------------------------------------------------- // vars // ---------------------------------------------------------------------- - + /** * @var array options array for an ldap connection including some base settings and DNs */ - private $_aLdap = array( + private array $_aLdap = [ 'server' => false, - 'port' => false, 'DnLdapUser' => false, // ldap rdn oder dn 'PwLdapUser' => false, 'DnUserNode' => false, // ou=People... 'DnAppNode' => false, // cn=AppGroup... 'protoVersion' => 3, 'debugLevel' => 0, - ); + ]; /** * @var object current ldap connection */ - private $_ldapConn = false; + private object|bool $_ldapConn = false; + + /** + * ldap bind object - bind was done? + * @var object|bool + */ + private object|bool $_ldapBind = false; /** - * @var bool bind was done? + * Flag if debug mode is on + * @var bool */ - private $_ldapBind = false; - var $bDebug = false; + var bool $bDebug = false; // ---------------------------------------------------------------------- // functions // ---------------------------------------------------------------------- - + /** * constructor * @param array $aConfig optional set ldap connection */ - public function __construct($aConfig = array()) { + public function __construct(array $aConfig = []) + { if (!function_exists("ldap_connect")) { die(__CLASS__ . " ERROR: php-ldap module is not installed on this server."); } @@ -58,7 +71,8 @@ class imlldap { } } - public function __destruct() { + public function __destruct() + { $this->close(); } @@ -72,7 +86,8 @@ class imlldap { * ldap config array * @see setConfig() */ - public function debugOn() { + public function debugOn(): void + { $this->bDebug = true; if ($this->_aLdap['debugLevel']) { $this->_w(__FUNCTION__ . ' setting debug level ' . $this->_aLdap['debugLevel']); @@ -83,7 +98,8 @@ class imlldap { /** * turn debug messages off */ - public function debugOff() { + public function debugOff(): void + { $this->bDebug = false; ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 0); } @@ -94,7 +110,8 @@ class imlldap { * @param string $sText message text * @return boolean */ - private function _w($sText) { + private function _w(string $sText): bool + { if (!$this->bDebug) { return false; } @@ -108,8 +125,9 @@ class imlldap { * @param string $sText message text * @return boolean */ - private function _wLdaperror($sText = '') { - $this->_w(($sText ? $sText . ' - ' : '' ) . 'last LDAP-ERROR: ' . ldap_error($this->_ldapConn)); + private function _wLdaperror(string $sText = ''): bool + { + $this->_w(($sText ? $sText . ' - ' : '') . 'last LDAP-ERROR: ' . ldap_error($this->_ldapConn)); return true; } @@ -118,19 +136,18 @@ class imlldap { // ---------------------------------------------------------------------- /** - * set a ldap config + * set a ldap config or modify existing value * - * @param array $aConfig new config items - * 'server' => 'ldaps://ldap.example.com', - * 'port' => 636, - * 'DnLdapUser' => 'cn=Lookup,ou=ServiceAccounts,dc=org,dc=example.com', // ldap rdn oder dn - * 'PwLdapUser' => 'IkHEFFzlZ...99j0h8WdI0LrLhxU', // password - * 'DnUserNode' => 'ou=People,ou=ORG,dc=org,dc=example.com', - * 'DnAppNode' => '' optional dn ... if a user must be member of a given group - * 'protoVersion' => 3 - * 'debugLevel' => 0 // for debugging set higher 0 AND call debugOn() - */ - public function setConfig($aConfig = array()) { + * @param array $aConfig new config items with these keys + * 'server' => 'ldaps://ldap.example.com', + * 'DnLdapUser' => 'cn=Lookup,ou=ServiceAccounts,dc=org,dc=example.com', // ldap rdn oder dn + * 'PwLdapUser' => 'PasswordOfLookupUser', // password + * 'DnUserNode' => 'ou=People,ou=ORG,dc=org,dc=example.com', + * 'protoVersion' => 3 + * 'debugLevel' => 0 // value for LDAP_OPT_DEBUG_LEVEL in debugOn() + */ + public function setConfig(array $aConfig = []): void + { if (is_array($aConfig)) { foreach (array_keys($this->_aLdap) as $sKey) { if (array_key_exists($sKey, $aConfig)) { @@ -148,7 +165,8 @@ class imlldap { /** * close an existing ldap connection */ - public function close() { + public function close(): void + { if ($this->_ldapConn) { $this->_w(__FUNCTION__ . ' closing connection.'); ldap_close($this->_ldapConn); @@ -162,7 +180,8 @@ class imlldap { /** * connect to ldap */ - public function connect() { + public function connect(): void + { if (!array_key_exists('server', $this->_aLdap) || !$this->_aLdap['server']) { die(__CLASS__ . " ERROR: no ldap server was setup set. Use setConfig() first."); @@ -172,8 +191,8 @@ class imlldap { $this->close(); } - $this->_w(__FUNCTION__ . ' connect to ' . $this->_aLdap['server'] . ':' . $this->_aLdap['port']); - $this->_ldapConn = ldap_connect($this->_aLdap['server'], $this->_aLdap['port']); + $this->_w(__FUNCTION__ . ' connect to ' . $this->_aLdap['server']); + $this->_ldapConn = ldap_connect($this->_aLdap['server']); if (!$this->_ldapConn) { $this->_wLdaperror(__FUNCTION__); die(__CLASS__ . " ERROR: ldap connect failed."); @@ -201,10 +220,11 @@ class imlldap { * @param string $sUser optional: username (overrides _aLdap['DnLdapUser']) * @param string $sPw optional: password (overrides _aLdap['PwLdapUser']) */ - public function bind($sUser = '', $sPw = '') { - if(!$sUser){ + public function bind(string $sUser = '', string $sPw = ''): bool + { + if (!$sUser) { $sUser = $this->_aLdap['DnLdapUser']; - $sPw = $this->_aLdap['PwLdapUser']; + $sPw = $this->_aLdap['PwLdapUser']; } if (!$this->_ldapConn) { @@ -218,7 +238,7 @@ class imlldap { $this->_w(__FUNCTION__ . ' ERROR: no user was set as first param.'); die("ERROR: no user was given to connect to ldap."); } - $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . substr($sPw,0,4).'**********'); + $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . substr($sPw, 0, 4) . '**********'); $this->_ldapBind = @ldap_bind($this->_ldapConn, $sUser, $sPw); if (!$this->_ldapBind) { @@ -232,7 +252,8 @@ class imlldap { /** * ldap unbind ... if a bind exists */ - public function unbind() { + public function unbind(): void + { if ($this->_ldapBind && !is_bool($this->_ldapBind)) { $this->_w(__FUNCTION__ . ' ...'); ldap_unbind($this->_ldapBind); @@ -251,30 +272,33 @@ class imlldap { * @param string $sDn DN to check * @return boolean */ - public function DnExists($sDn) { - $aData = $this->searchDn($sDn, '(&(objectclass=top))', array("*")); + public function DnExists(string $sDn): bool + { + $aData = $this->searchDn($sDn, '(&(objectclass=top))', ["*"]); return is_array($aData); } /** * get simpler array from ldap_get_entries after ldap_search + * If the given array doesn't contain the key "dn" it returns "false" * - * @param array $aRecord singel result item + * @param array $aRecord single result item * @return array */ - public function normalizeSearchentry($aRecord) { - if (!is_array($aRecord) || !isset($aRecord['dn'])){ + public function normalizeSearchentry(array $aRecord): bool|array + { + if (!is_array($aRecord) || !isset($aRecord['dn'])) { return false; } - $aItem = array(); + $aItem = []; unset($aRecord['count']); foreach ($aRecord as $sAttr => $aData) { if (!is_integer($sAttr)) { $value = $aData; if (is_array($aData)) { unset($aData['count']); - $bUseArray=count($aData)>1 || array_search($sAttr, array('hieradata', 'member', 'memberof', 'objectclass'))!==false; - if($bUseArray){ + $bUseArray = count($aData) > 1 || array_search($sAttr, ['hieradata', 'member', 'memberof', 'objectclass']) !== false; + if ($bUseArray) { sort($aData); } $value = $bUseArray ? $aData : $aData[0]; @@ -284,23 +308,6 @@ class imlldap { } return $aItem; } - /** - * get simpler array from ldap_get_entries after ldap_search - * - * @param array $aRecord singel result item - * @return array - */ - public function normalizeSearchresult($aLdapSearchresult) { - if (!is_array($aLdapSearchresult)){ - return false; - } - $aReturn = array(); - unset($aRecord['count']); - foreach ($aLdapSearchresult as $aRecord) { - $aReturn[]=$this->normalizeSearchentry($aRecord); - } - return $aReturn; - } /** * sanitize value to put into a search filter @@ -315,51 +322,56 @@ class imlldap { * @param string $s value to sanitize * @return string */ - static public function sanitizeFilter($s){ + static public function sanitizeFilter(string $s): string + { // helper array to replace special chars - $aReplace=array(); - for($i=0; $i<65; $i++){ - $val=dechex($i); - if ($val<10){ - $val="0$val"; + $aReplace = []; + for ($i = 0; $i < 65; $i++) { + $val = dechex($i); + if ($val < 10) { + $val = "0$val"; } - $aReplace[chr($i)]='\\'.$val; + $aReplace[chr($i)] = '\\' . $val; } - $sReturn=$s; - $sReturn=str_replace(array_keys($aReplace), array_values($aReplace), $sReturn); - + $sReturn = $s; + $sReturn = str_replace(array_keys($aReplace), array_values($aReplace), $sReturn); + return $sReturn; } /** - * search in ldap directory and get result as array + * search in ldap directory and get result as array. + * It returns "false" on error: + * - no ldap connection + * - search failed * * @param string $sDn DN to search for * @param string $sSearchFilter filter in ldap filter syntax * @param array $aAttributesToGet flat array of attributes to fetch * @param boolean $bRecursive recusrive (uses ldap_search) or not (ldap_list) - * @return array + * @return boolean|array */ - public function searchDn($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*"), $bRecursive=true) { + public function searchDn(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array + { if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } - $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1).' recursive = '.($bRecursive ? 'yes' : 'no' )); + $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1) . ' recursive = ' . ($bRecursive ? 'yes' : 'no')); $oLdapSearch = $bRecursive - ? ldap_search($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet) - : ldap_list ($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet) - ; + ? ldap_search($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet) + : ldap_list($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet) + ; if (!$oLdapSearch) { $this->_w(__FUNCTION__ . " !!!ERROR!!! filter $sSearchFilter failed "); return false; } $aItems = ldap_get_entries($this->_ldapConn, $oLdapSearch); - $this->_w(__FUNCTION__ . " count of returned items: ".count($aItems)); + $this->_w(__FUNCTION__ . " count of returned items: " . count($aItems)); // $this->_w(__FUNCTION__ . " <pre>".print_r($aItems,1).'</pre>'); return $aItems; } @@ -371,9 +383,10 @@ class imlldap { * @param array $aAttributesToGet flat array of attributes to fetch * @param bool $bRecursive flag: recursive search? default: true (=yes, recursive) * - * @return array + * @return boolean|array */ - public function searchUser($sSearchFilter='', $aAttributesToGet = array("*"), $bRecursive=true) { + public function searchUser(string $sSearchFilter = '', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array + { return $this->searchDn($this->_aLdap['DnUserNode'], $sSearchFilter, $aAttributesToGet, $bRecursive); /* if (!$this->_ldapBind) { @@ -396,13 +409,14 @@ class imlldap { * It returns false if the user does not exist or is * not member of the group 'DnAppNode' (if it was set). * - * @param string $sUser user id (uid) or email (mail) to search - * @param array $aAttributesToGet i.e. array("ou", "sn", "vorname", "mail", "uid", "memberOf") + * @param string $sUser user id (uid) or email (mail) to search + * @param array $aAttributesToGet i.e. ["ou", "sn", "vorname", "mail", "uid", "memberOf"] * @return boolean|array */ - public function getUserInfo($sUser, $aAttributesToGet = array("*")) { + public function getUserInfo(string $sUser, array $aAttributesToGet = ["*"]): bool|array + { if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } @@ -430,12 +444,13 @@ class imlldap { * email address. It returns false if the user does not exist or is * not member of the group 'DnAppNode' (if it was set). * - * @param type $sUser + * @param string $sUser * @return string */ - public function getUserDn($sUser) { + public function getUserDn(string $sUser): bool|string + { $this->_w(__FUNCTION__ . '(' . $sUser . ')'); - $aItem = $this->getUserInfo($sUser, array("dn")); + $aItem = $this->getUserInfo($sUser, ["dn"]); if (is_array($aItem) && array_key_exists('dn', $aItem)) { $this->_w(__FUNCTION__ . ' OK: dn was found ' . $aItem['dn']); return $aItem['dn']; @@ -452,15 +467,16 @@ class imlldap { * @param string $sPW password * @return boolean */ - public function setPassword($sUser, $sPW) { + public function setPassword(string $sUser, string $sPW): bool + { if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } $sDn = $this->getUserDn($sUser); if ($sDn) { - if (!ldap_mod_replace($this->_ldapConn, $sDn, array('userpassword' => "{MD5}" . base64_encode(pack("H*", md5($sPW)))))) { + if (!ldap_mod_replace($this->_ldapConn, $sDn, ['userpassword' => "{MD5}" . base64_encode(pack("H*", md5($sPW)))])) { $this->_wLdaperror(__FUNCTION__); return false; } else { @@ -478,17 +494,18 @@ class imlldap { * @param string $Input * @return string */ - private function _getNTLMHash($Input) { + private function _getNTLMHash(string $Input): string + { // Convert the password from UTF8 to UTF16 (little endian) $Input = iconv('UTF-8', 'UTF-16LE', $Input); // Encrypt it with the MD4 hash - $MD4Hash=hash('md4',$Input); + $MD4Hash = hash('md4', $Input); // Make it uppercase, not necessary, but it's common to do so with NTLM hashes $NTLMHash = strtoupper($MD4Hash); // Return the result - return($NTLMHash); + return ($NTLMHash); } /** @@ -502,15 +519,19 @@ class imlldap { * @param string $sPW password * @return boolean */ - public function setPasswordSamba($sUser, $sPW) { + public function setPasswordSamba(string $sUser, string $sPW): bool + { $sDn = $this->getUserDn($sUser); if ($sDn) { $sPwField = 'sambaNTPassword'; $sPwValue = $this->_getNTLMHash($sPW); - return $this->objUpdate($sDn, array( - $sPwField => $sPwValue, - 'SambaPwdLastSet' => date('U'), - )); + return $this->objUpdate( + $sDn, + [ + $sPwField => $sPwValue, + 'SambaPwdLastSet' => date('U'), + ] + ); } $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser); return false; @@ -519,15 +540,17 @@ class imlldap { /** * update an ldap object * this requires a ldap bind with master/ admin account + * It returns true if the action was successful * * @param string $sDn dn to update * @param array $aItem array of new ldap properties * @return boolean */ - public function objAdd($sDn, $aItem) { - $this->_w(__FUNCTION__ . '("' . $sDn . '", <pre>['.print_r($aItem, 1).']</pre>)'); + public function objAdd(string $sDn, array $aItem): bool + { + $this->_w(__FUNCTION__ . '("' . $sDn . '", <pre>[' . print_r($aItem, 1) . ']</pre>)'); if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } @@ -543,13 +566,14 @@ class imlldap { * this requires a ldap bind with master/ admin account * * @param string $sDn dn to update - * @param string $aItem array of new ldap properties + * @param array $aItem array of new ldap properties * @return boolean */ - public function objAddAttr($sDn, $aItem) { + public function objAddAttr(string $sDn, array $aItem): bool + { $this->_w(__FUNCTION__ . '("' . $sDn . '", [array])'); if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } @@ -567,23 +591,27 @@ class imlldap { return false; } - /** + /** * read attributes from ldap node with given DN (using ldap_read) + * It returns "false" if the action was not successful + * - no ldap connection + * - DN or filter didn't match * * @param string $sDn DN to search for * @param string $sSearchFilter filter in ldap filter syntax * @param array $aAttributesToGet flat array of attributes to fetch - * @return array + * @return boolean|array */ - public function objGet($sDn, $sSearchFilter='(objectclass=*)', $aAttributesToGet = array("*")) { + public function objGet(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"]): bool|array + { - $this->_w(__FUNCTION__ . '("' . $sDn . '", filter = '.$sSearchFilter.', atttr= '.print_r($aAttributesToGet, 1).' )'); + $this->_w(__FUNCTION__ . '("' . $sDn . '", filter = ' . $sSearchFilter . ', atttr= ' . print_r($aAttributesToGet, 1) . ' )'); if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } - + $oLdapResult = ldap_read($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet); if (!$oLdapResult) { @@ -597,15 +625,17 @@ class imlldap { * update an ldap object with given key-value array * if the attribute (key) does not exist it will be created. * this requires a ldap bind with master/ admin account + * It returns "false" if the action failed * * @param string $sDn full DN where to update the item * @param array $aItem updated entry * @return boolean */ - public function objUpdate($sDn, $aItem) { + public function objUpdate(string $sDn, array $aItem): bool + { $this->_w(__FUNCTION__ . '("' . $sDn . '", ' . print_r($aItem, 1) . ')'); if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } @@ -624,14 +654,16 @@ class imlldap { /** * delete an ldap object * this requires a ldap bind with master/ admin account + * It returns "false" if the action failed * * @param string $sDn full DN to remove * @return boolean */ - public function objDelete($sDn) { + public function objDelete(string $sDn): bool + { $this->_w(__FUNCTION__ . '("' . $sDn . '")'); if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } @@ -640,7 +672,8 @@ class imlldap { if (!ldap_delete($this->_ldapConn, $sDn)) { $this->_wLdaperror(__FUNCTION__); return false; - } return true; + } + return true; } $this->_w(__FUNCTION__ . ' missing parameter for DN'); return false; @@ -649,17 +682,21 @@ class imlldap { /** * delete attributes of an ldap object * this requires a ldap bind with master/ admin account + * It returns "false" if the action failed * - * TODO: Test me + * @example: + * remove attribute "userPassword" of user $sUserDn: + * <code>$oLdap->objDeleteAttr($sUserDn, ['userPassword'=>[]]</code> * * @param string $sDn DN - * @param string $aItem item to remove + * @param array $aItem item to remove * @return boolean */ - public function objDeleteAttr($sDn, $aItem) { + public function objDeleteAttr(string $sDn, array $aItem): bool + { $this->_w(__FUNCTION__ . '("' . $sDn . '", [array])'); if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } @@ -669,7 +706,8 @@ class imlldap { if (!ldap_mod_del($this->_ldapConn, $sDn, $aItem)) { $this->_wLdaperror(__FUNCTION__); return false; - } return true; + } + return true; } $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) or item was not an array ' . print_r($aItem, 1)); return false; @@ -683,15 +721,16 @@ class imlldap { * @param string $sAttrValue value to check * @return boolean */ - public function objectAttributeExists($sDn, $sAttribute) { + public function objectAttributeExists(string $sDn, string $sAttribute): bool + { $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '")'); if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } - $aData = $this->searchDn($sDn, '(&(objectclass=top))', array($sAttribute)); + $aData = $this->searchDn($sDn, '(&(objectclass=top))', [$sAttribute]); $return = (is_array($aData) && isset($aData[0][strtolower($sAttribute)])); $this->_w(__FUNCTION__ . '(...) returns ' . ($return ? 'true' : 'false')); return $return; @@ -705,15 +744,16 @@ class imlldap { * @param string $sAttrValue value to check * @return boolean */ - public function objectAttributeAndValueExist($sDn, $sAttribute, $sAttrValue) { + public function objectAttributeAndValueExist(string $sDn, string $sAttribute, string $sAttrValue): bool + { $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '", "' . $sAttrValue . '")'); if (!$this->_ldapBind) { - if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ + if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } - $aData = $this->searchDn($sDn, '(&(objectclass=top))', array($sAttribute)); + $aData = $this->searchDn($sDn, '(&(objectclass=top))', [$sAttribute]); $return = (is_array($aData) && isset($aData[0][strtolower($sAttribute)]) && array_search($sAttrValue, $aData[0][strtolower($sAttribute)]) !== false); $this->_w(__FUNCTION__ . '(...) returns ' . ($return ? 'true' : 'false')); return $return; @@ -728,7 +768,8 @@ class imlldap { * @param string $sAttrValue value to check * @return boolean */ - public function objectAttributeAndValueMustExist($sDn, $sAttribute, $sAttrValue) { + public function objectAttributeAndValueMustExist(string $sDn, string $sAttribute, string $sAttrValue): bool + { $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '", "' . $sAttrValue . '")'); // return if it already exists if ($this->objectAttributeAndValueExist($sDn, $sAttribute, $sAttrValue)) { @@ -737,7 +778,7 @@ class imlldap { // create it $this->_w(__FUNCTION__ . " create $sAttribute = $sAttrValue"); - $return = $this->objAddAttr($sDn, array($sAttribute => $sAttrValue)); + $return = $this->objAddAttr($sDn, [$sAttribute => $sAttrValue]); return $return; } @@ -749,7 +790,8 @@ class imlldap { * @param string $sDn optional DN where to create the user * @return boolean */ - public function userAdd($aItem, $sDn = false) { + public function userAdd(array $aItem, string $sDn = ""): bool + { if (!$sDn) { $sDn = 'cn=' . $aItem['cn'] . ',' . $this->_aLdap['DnUserNode']; } @@ -769,7 +811,8 @@ class imlldap { * @param string $sPW new password to set * @return boolean */ - public function userDelete($sUserDn) { + public function userDelete(string $sUserDn): bool + { $this->_w(__FUNCTION__ . '(' . $sUserDn . ')'); return $this->objDelete($sUserDn); } @@ -781,7 +824,8 @@ class imlldap { * @param array $aItem new user data to update * @return boolean */ - public function userUpdate($aItem) { + public function userUpdate(array $aItem): bool + { $this->_w(__FUNCTION__ . '([array])'); $sDn = $this->getUserDn($aItem['uid']); if ($sDn) { @@ -801,7 +845,8 @@ class imlldap { * @param string $sPW password * @return boolean */ - public function verifyPassword($sUser, $sPW) { + public function verifyPassword(string $sUser, string $sPW): bool + { $sDn = $this->getUserDn($sUser); if ($sDn) { return $this->bind($sDn, $sPW); diff --git a/public_html/deployment/classes/logger.class.php b/public_html/deployment/classes/logger.class.php index a25fcc959bdfc1ea6681c56a093d02edc1583c57..3cd48d4911471734c7636507e1a71a137fd9848b 100644 --- a/public_html/deployment/classes/logger.class.php +++ b/public_html/deployment/classes/logger.class.php @@ -27,15 +27,18 @@ * 2022-10-16 mark longest action with an icon * 2022-12-15 make it compatible to PHP 8.2; add doc + comments * 2023-05-15 fix _getBar() - division by zero + * 2024-07-12 php8 only: use variable types; update phpdocs + * 2024-09-04 fix short array syntax * ---------------------------------------------------------------------- */ -class logger { +class logger +{ /** * @var {array} array of added messages */ protected $aMessages = []; - + /** * @var {bool} flag: show debug infos? default: false */ @@ -51,21 +54,22 @@ class logger { */ protected $sCssPrefix = ''; + protected $sSourceUrl = 'https://github.com/axelhahn/ahlogger'; + // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- /** - * constuctor + * Constuctor * @param string $sInitMessage init message - * @return boolean */ - public function __construct($sInitMessage = "Logger was initialized.") { - $this->_iMemStart=memory_get_usage(); + public function __construct(string $sInitMessage = "Logger was initialized.") + { + $this->_iMemStart = memory_get_usage(); $this->enableDebug(true); $this->add($sInitMessage); - $this->sCssPrefix='debug-'.md5(microtime(true)); - return true; + $this->sCssPrefix = 'debug-' . md5(microtime(true)); } // ---------------------------------------------------------------------- @@ -73,51 +77,55 @@ class logger { // ---------------------------------------------------------------------- /** - * add a logging message - * @param type $sMessage - * @param type $sLevel + * Add a logging message + * @param string $sMessage + * @param string $sLevel * @return boolean */ - public function add($sMessage, $sLevel = "info") { - if (!$this->bShowDebug){ + public function add(string $sMessage, string $sLevel = "info"): bool + { + if (!$this->bShowDebug) { return false; - } - $this->aMessages[] = array( + } + $this->aMessages[] = [ 'time' => microtime(true), 'message' => $sMessage, 'level' => preg_replace('/[^a-z0-9\-\_]/', '', $sLevel), 'memory' => memory_get_usage() - ); + ]; return true; } /** - * enable / disable debugging - * @param type $bEnable - * @return type + * Enable / disable debugging + * @param bool $bEnable + * @return bool */ - public function enableDebug($bEnable=true){ - return $this->bShowDebug=!!$bEnable; + public function enableDebug(bool $bEnable = true): bool + { + return $this->bShowDebug = !!$bEnable; } /** - * enable client debugging by a given array of allowed ip addresses + * Enable client debugging by a given array of allowed ip addresses * @param array $aIpArray list of ip addresses in a flat array * @return boolean */ - public function enableDebugByIp($aIpArray){ + public function enableDebugByIp(array $aIpArray): bool + { $this->enableDebug(false); - if (!$_SERVER || !is_array($_SERVER) || !array_key_exists("REMOTE_ADDR", $_SERVER)){ + if (!$_SERVER || !is_array($_SERVER) || !array_key_exists("REMOTE_ADDR", $_SERVER)) { return false; } - if (array_search($_SERVER['REMOTE_ADDR'], $aIpArray)!==false){ + if (array_search($_SERVER['REMOTE_ADDR'], $aIpArray) !== false) { $this->enableDebug(true); } + return true; } /** - * helper function: prepare array of added massages before output + * Helper function: prepare array of added massages before output * - detect warnings and errors * - detect needed time for each action * - detect longest action @@ -126,14 +134,15 @@ class logger { * * @return array */ - protected function _prepareRendering(){ - $iMem=memory_get_usage(); + protected function _prepareRendering(): array + { + $iMem = memory_get_usage(); $this->add('<hr>'); $this->add('Memory on start: ' . number_format($this->_iMemStart, 0, '.', ',') . " bytes"); - $this->add('Memory on end: ' . number_format($iMem, 0, '.', ',') . " bytes"); - $this->add('Memory peak: ' . number_format(memory_get_peak_usage(), 0, '.', ',') . " bytes"); + $this->add('Memory on end: ' . number_format($iMem, 0, '.', ',') . " bytes"); + $this->add('Memory peak: ' . number_format(memory_get_peak_usage(), 0, '.', ',') . " bytes"); - $aReturn=[ + $aReturn = [ 'totaltime' => false, 'level' => false, 'warnings' => '', @@ -154,74 +163,77 @@ class logger { foreach ($this->aMessages as $aLogentry) { $iCounter++; - if($aLogentry["level"]=="warning"){ - $bHasWarning=true; + if ($aLogentry["level"] == "warning") { + $bHasWarning = true; } - if($aLogentry["level"]=="error"){ - $bHasError=true; + if ($aLogentry["level"] == "error") { + $bHasError = true; } - $sTrId = $this->sCssPrefix.'debugTableRow' . $iCounter; + $sTrId = $this->sCssPrefix . 'debugTableRow' . $iCounter; $iDelta = $aLogentry["time"] - $iLasttime; if ($iDelta > $iMaxtime) { $iMaxtime = $iDelta; $sMaxRowId = $sTrId; } - $iMaxmem=max($aLogentry["memory"], $iMaxmem); + $iMaxmem = max($aLogentry["memory"], $iMaxmem); if (($iDelta > 1) || $aLogentry["level"] == "warning") { - $aReturn['warnings'].='<a href="#' . $sTrId . '" title="' . sprintf("%01.4f", $iDelta) . ' s">' . $iCounter . '</a> '; + $aReturn['warnings'] .= '<a href="#' . $sTrId . '" title="' . sprintf("%01.4f", $iDelta) . ' s">' . $iCounter . '</a> '; } if ($aLogentry["level"] == "error") { - $aReturn['errors'].='<a href="#' . $sTrId . '" title="' . sprintf("%01.4f", $iDelta) . ' s">' . $iCounter . '</a> '; + $aReturn['errors'] .= '<a href="#' . $sTrId . '" title="' . sprintf("%01.4f", $iDelta) . ' s">' . $iCounter . '</a> '; } - $aReturn['entries'][]=[ - 'time'=>$aLogentry["time"], - 'level'=>$aLogentry["level"], - 'message'=>$aLogentry["message"], - 'memory'=>sprintf("%01.2f", $aLogentry["memory"]/1024/1024), // MB + $aReturn['entries'][] = [ + 'time' => $aLogentry["time"], + 'level' => $aLogentry["level"], + 'message' => $aLogentry["message"], + 'memory' => sprintf("%01.2f", $aLogentry["memory"] / 1024 / 1024), // MB - 'trid'=>$sTrId, - 'trclass'=>$aLogentry["level"], - 'counter'=>$iCounter, - 'timer'=>sprintf("%01.3f", $aLogentry["time"] - $sStarttime), - 'delta'=>sprintf("%01.0f", $iDelta*1000), + 'trid' => $sTrId, + 'trclass' => $aLogentry["level"], + 'counter' => $iCounter, + 'timer' => sprintf("%01.3f", $aLogentry["time"] - $sStarttime), + 'delta' => sprintf("%01.0f", $iDelta * 1000), ]; $iLasttime = $aLogentry["time"]; } - $aReturn['level']=($bHasWarning + $aReturn['level'] = ($bHasWarning ? ($bHasError ? 'error' : 'warning') : '' ); - $aReturn['maxrowid']=$sMaxRowId; - $aReturn['maxtime']=sprintf("%01.3f", $iMaxtime); - $aReturn['maxmem']=sprintf("%01.2f", $iMaxmem/1024/1024); - $aReturn['totaltime']=sprintf("%01.3f", $aLogentry['time']-$aReturn['entries'][0]['time']); + $aReturn['maxrowid'] = $sMaxRowId; + $aReturn['maxtime'] = sprintf("%01.3f", $iMaxtime); + $aReturn['maxmem'] = sprintf("%01.2f", $iMaxmem / 1024 / 1024); + $aReturn['totaltime'] = sprintf("%01.3f", $aLogentry['time'] - $aReturn['entries'][0]['time']); return $aReturn; } /** - * get html code for a progressbar with divs - * @param {int|float} $iVal value between 0..max value - * @param {int|float} $iMax max value - * @return {string} + * Get html code for a progressbar with divs + * @param int|float $iVal value between 0..max value + * @param int|float $iMax max value + * @return string */ - protected function _getBar($iVal, $iMax){ - return $iMax>0 - ? '<div class="bar"><div class="progress" style="width: '.($iVal/$iMax*100).'%;"> </div></div>' + protected function _getBar(int|float $iVal, int|float $iMax): string + { + return $iMax > 0 + ? '<div class="bar"><div class="progress" style="width: ' . ($iVal / $iMax * 100) . '%;"> </div></div>' : '' - ; + ; } /** - * render output of all logging messages + * Render output of all logging messages + * @return string */ - public function render() { - if (!$this->bShowDebug){ + public function render(): string + { + if (!$this->bShowDebug) { return false; } - $aData=$this->_prepareRendering(); + $aData = $this->_prepareRendering(); /* Array @@ -252,57 +264,58 @@ class logger { ) */ - $sOut=''; + $sOut = ''; // echo '<pre>'; print_r($aData); die(); - foreach ($aData['entries'] as $aLogentry){ - $sOut.='<tr class="'.$this->sCssPrefix.'-level-' . $aLogentry["level"] . ''.($aLogentry["trid"]==$aData["maxrowid"] ? ' '.$this->sCssPrefix.'-maxrow' : '').'" ' - .'id="' . $aLogentry["trid"] . '">' . - '<td>' . $aLogentry["counter"] . '</td>' . - '<td>' . $aLogentry["level"] . '</td>' . - '<td>' . $aLogentry["timer"] . '</td>' . - '<td>' . $this->_getBar($aLogentry["delta"], $aData["maxtime"]*1000). $aLogentry["delta"] .' ms'.($aLogentry["delta"]==$aData['maxtime']*1000 ? ' ⏱️' : '').'</td>' . - '<td>' . $this->_getBar($aLogentry["memory"], $aData["maxmem"]) . $aLogentry["memory"] .' MB'. '</td>' . - '<td>' . $aLogentry["message"] . '</td>' . - '</tr>'; + foreach ($aData['entries'] as $aLogentry) { + $sOut .= '<tr class="' . $this->sCssPrefix . '-level-' . $aLogentry["level"] . '' . ($aLogentry["trid"] == $aData["maxrowid"] ? ' ' . $this->sCssPrefix . '-maxrow' : '') . '" ' + . 'id="' . $aLogentry["trid"] . '">' . + '<td align="right">' . $aLogentry["counter"] . '</td>' . + '<td>' . $aLogentry["level"] . '</td>' . + '<td align="right">' . $aLogentry["timer"] . '</td>' . + '<td align="right">' . $this->_getBar($aLogentry["delta"], $aData["maxtime"] * 1000) . ($aLogentry["delta"] == $aData['maxtime'] * 1000 ? '⏱️ ' : '') . $aLogentry["delta"] . ' ms</td>' . + '<td align="right">' . $this->_getBar($aLogentry["memory"], $aData["maxmem"]) . $aLogentry["memory"] . ' MB' . '</td>' . + '<td>' . $aLogentry["message"] . '</td>' . + '</tr>'; } - if ($sOut){ + if ($sOut) { $sOut = ' <style> - .'.$this->sCssPrefix.'-info {position: fixed; top: 6em; right: 1em; background: rgba(200,228,255, 0.8); border: 1px solid; z-index: 99999;} - .'.$this->sCssPrefix.'-info .head {background: rgba(0,0,0,0.4); color: #fff;padding: 0em 0.5em 0.2em; } - .'.$this->sCssPrefix.'-info .content {padding: 0.5em; } - .'.$this->sCssPrefix.'-info .content .total {font-size: 160%; color: rgba(0,0,0,0.5); margin: 0.3em 0; display: inline-block;} + .' . $this->sCssPrefix . '-info {position: fixed; top: 6em; right: 1em; background: rgba(230,240,255, 0.8); border: 2px solid rgba(0,0,0,0.2); border-radius: 0.3em; z-index: 99999;} + .' . $this->sCssPrefix . '-info .loggerhead {background: rgba(0,0,0,0.4); color: #fff;padding: 0em 0.5em 0.2em; border-radius: 0.3em 0.3em 0 0; } + .' . $this->sCssPrefix . '-info .loggercontent {padding: 0.5em; } + .' . $this->sCssPrefix . '-info .loggercontent .total {font-size: 160%; color: rgba(0,0,0,0.5); margin: 0.3em 0; display: inline-block;} - .'.$this->sCssPrefix.'-messages {margin: 5em 2em 2em;} - .'.$this->sCssPrefix.'-messages .bar {background: rgba(0,0,0,0.03); height: 1.4em; position: absolute; width: 6em; border-right: 1px solid rgba(0,0,0,0.2);} - .'.$this->sCssPrefix.'-messages .progress {background: rgba(100,140,180,0.2); height: 1.4em; padding: 0;} - .'.$this->sCssPrefix.'-messages table{background: #fff; color: #222;table-layout:fixed; } - .'.$this->sCssPrefix.'-messages table th{background: none;} - .'.$this->sCssPrefix.'-messages table th.barcol{min-width: 7em; position: relative;} - .'.$this->sCssPrefix.'-messages table td{padding: 3px; vertical-align: top;} - .'.$this->sCssPrefix.'-messages table th:hover{background:#aaa !important;} + .' . $this->sCssPrefix . '-messages {margin: 5em 2em 2em;} + .' . $this->sCssPrefix . '-messages>h3 {font-size: 150%; margin: 0 0 0.5em 0;} + .' . $this->sCssPrefix . '-messages .bar {background: rgba(0,0,0,0.03); height: 1.4em; position: absolute; width: 6em; border-right: 1px solid rgba(0,0,0,0.2);} + .' . $this->sCssPrefix . '-messages .progress {background: rgba(100,140,180,0.2); height: 1.4em; padding: 0; float: left;} + .' . $this->sCssPrefix . '-messages table{background: #fff; color: #222;table-layout:fixed; border: 2px solid rgba(0,0,0,0.2); border-radius: 0.5em;} + .' . $this->sCssPrefix . '-messages table th{background: none; color: #222; border-bottom: 2px solid rgba(0,0,0,0.4);} + .' . $this->sCssPrefix . '-messages table th.barcol{min-width: 7em; position: relative;} + .' . $this->sCssPrefix . '-messages table td{padding: 3px; vertical-align: top;} + .' . $this->sCssPrefix . '-messages table th:hover{background:#aaa !important;} - .'.$this->sCssPrefix.'-level-info{background: #e0e8f8; color:#124} - .'.$this->sCssPrefix.'-level-warning{background: #fcf8e3; color: #980;} - .'.$this->sCssPrefix.'-level-error{background: #fce0e0; color: #944;} - .'.$this->sCssPrefix.'-maxrow{color:#f33; font-weight: bold;} + .' . $this->sCssPrefix . '-level-info{background: #f0f4f4; color:#124} + .' . $this->sCssPrefix . '-level-warning{background: #fcf8e3; color: #980;} + .' . $this->sCssPrefix . '-level-error{background: #fce0e0; color: #944;} + .' . $this->sCssPrefix . '-maxrow{color:#f33; font-weight: bold;} </style> - <div class="'.$this->sCssPrefix.' '.$this->sCssPrefix.'-info '.$this->sCssPrefix.'-level-'.$aData['level'].'"> - <div class="head">ahLogger</div> - <div class="content"> + <div class="' . $this->sCssPrefix . ' ' . $this->sCssPrefix . '-info ' . $this->sCssPrefix . '-level-' . $aData['level'] . '" onclick="location.href=\'#' . $this->sCssPrefix . '-messages\';"> + <div class="loggerhead">ahLogger</div> + <div class="loggercontent"> <span class="total">⏱️ ' . $aData['totaltime'] . ' s</span><br> - 🪲 <a href="#'.$this->sCssPrefix.'-messages">Debug infos</a> | 🔺 <a href="#">top</a><br> - <span>longest action: ⏱️ <a href="#' . $aData['maxrowid'] . '">' . ($aData['maxtime']*1000) . ' ms</a></span> - ' . ($aData['errors'] ? '<br><span>‼️ Errors: '.$aData['errors'] . '</span>' : '').' - ' . ($aData['warnings'] ? '<br><span>⚠️ Warnings: '.$aData['warnings'] . '</span>' : '').' + 🪲 <a href="#' . $this->sCssPrefix . '-messages">Debug infos</a> | 🔺 <a href="#">top</a><br> + <span>longest action: ⏱️ <a href="#' . $aData['maxrowid'] . '">' . ($aData['maxtime'] * 1000) . ' ms</a></span> + ' . ($aData['errors'] ? '<br><span>‼️ Errors: ' . $aData['errors'] . '</span>' : '') . ' + ' . ($aData['warnings'] ? '<br><span>⚠️ Warnings: ' . $aData['warnings'] . '</span>' : '') . ' </div> </div> - <div id="'.$this->sCssPrefix.'-messages" class="'.$this->sCssPrefix.' '.$this->sCssPrefix.'-messages"> - DEBUG :: LOG MESSAGES<br>' - . ($aData['errors'] ? '<span>Errors: '.$aData['errors'] . '</span><br>' : '') - . ($aData['warnings'] ? '<span>Warnings: '.$aData['warnings'] . '</span><br>' : '') - .'<br> + <div id="' . $this->sCssPrefix . '-messages" class="' . $this->sCssPrefix . ' ' . $this->sCssPrefix . '-messages"> + <h3>ahLogger 🪳 Debug messages</h3>' + . ($aData['errors'] ? '<span>Errors: ' . $aData['errors'] . '</span><br>' : '') + . ($aData['warnings'] ? '<span>Warnings: ' . $aData['warnings'] . '</span><br>' : '') + . '<br> <table > <thead> <tr> @@ -313,32 +326,36 @@ class logger { <th class="barcol">memory</th> <th>message</th> </tr></thead><tbody> - ' . $sOut . '</tbody></table>' + ' . $sOut + . '</tbody></table>' + . '🌐 <a href="'.$this->sSourceUrl.'" target="_blank">'.$this->sSourceUrl.'</a>' ; - } + } return $sOut; } - /** - * render output of all logging messages for cli output + + /** + * Render output of all logging messages for cli output * @return string */ - public function renderCli(){ - if (!$this->bShowDebug){ + public function renderCli(): string + { + if (!$this->bShowDebug) { return false; } - $aData=$this->_prepareRendering(); + $aData = $this->_prepareRendering(); - $sOut=''; - foreach ($aData['entries'] as $aLogentry){ - $sOut.=$aLogentry["timer"].' | ' - .$aLogentry["delta"].' ms | ' - .$aLogentry["level"].' | ' - .(sprintf("%01.3f", $aLogentry["memory"]/1024/1024)).' MB | ' - .$aLogentry["message"].' ' - . "\n" - ; + $sOut = ''; + foreach ($aData['entries'] as $aLogentry) { + $sOut .= $aLogentry["timer"] . ' | ' + . $aLogentry["delta"] . ' ms | ' + . $aLogentry["level"] . ' | ' + . (sprintf("%01.3f", $aLogentry["memory"] / 1024 / 1024)) . ' MB | ' + . $aLogentry["message"] . ' ' + . "\n" + ; } - $sOut.="\nTotal time: ".$aData['totaltime'] . "\n"; + $sOut .= "\nTotal time: " . $aData['totaltime'] . "\n"; return $sOut; } } diff --git a/public_html/deployment/classes/messenger.class.php b/public_html/deployment/classes/messenger.class.php index f888cefbadd64ea35d841a27788db0d6f4f80525..3e8caac7378a1b6856a3be6bb5450dad0486847f 100644 --- a/public_html/deployment/classes/messenger.class.php +++ b/public_html/deployment/classes/messenger.class.php @@ -4,70 +4,80 @@ * send messenger notifications * * @author hahn + * + * 2024-08-23 v1.1 Axel Hahn php8 only; added variable types */ -class messenger { +class messenger +{ /** * config array for messengers - * @var type + * @var array */ - protected $_aCfg = array(); - + protected array $_aCfg = []; + /** * content of messagetext to send * @var string */ - protected $_sMessage = ''; + protected string $_sMessage = ''; /** + * Constructor * * @example - * $oMessenger = new messenger(array( - * 'slack'=>array( + * $oMessenger = new messenger([ + * 'slack'=>[ * 'incomingurl'=>[WebHook url], * 'user'=>[visible username in slack], * 'icon'=>[Slack Icon], // hm, does not seem to work - * ), - * 'email'=>array( + * ], + * 'email'=>[ * 'from'=>[senders e-mail] * 'to'=>[email-address(es)] // multiple emails must be delimited with ";" - * ) - * )); + * ] + * ]); * @param array $aCfg config array for notification targets - * @return boolean */ - public function __construct($aCfg) { + public function __construct(array $aCfg) + { $this->_aCfg = $aCfg; - return true; } /** - * send an email if _aCfg['email']['to'] exists + * Send an email if _aCfg['email']['to'] exists + * @return bool */ - private function _sendEmail(){ + private function _sendEmail(): bool + { if (isset($this->_aCfg['email']['to']) && $this->_aCfg['email']['to']) { preg_match('/^(.*)\n/', $this->_sMessage, $aTmp); $sSubject = $aTmp[0]; return mail( - $this->_aCfg['email']['to'], - $sSubject, $this->_sMessage, + $this->_aCfg['email']['to'], + $sSubject, + $this->_sMessage, "From: " . $this->_aCfg['email']['from'] . "\r\n" . "Reply-To: " . $this->_aCfg['email']['from'] . "\r\n" ); } return false; } - + /** - * send a message to slack if _aCfg['slack']['incomingurl'] exists + * Send a message to slack if _aCfg['slack']['incomingurl'] exists + * @return bool|string */ - private function _sendToSlack(){ + private function _sendToSlack(): bool|string + { if (isset($this->_aCfg['slack']['incomingurl']) && $this->_aCfg['slack']['incomingurl']) { require_once(__DIR__ . '/../../vendor/shooker/shooker.php'); $shkr = new Shooker(); $shkr->setupIncoming($this->_aCfg['slack']['incomingurl']); - $sUser=(isset($this->_aCfg['slack']['user'])? $this->_aCfg['slack']['user']: false); - $sIcon=(isset($this->_aCfg['slack']['icon'])? $this->_aCfg['slack']['icon']: false); + $sUser = (isset($this->_aCfg['slack']['user']) ? $this->_aCfg['slack']['user'] : false); + $sIcon = (isset($this->_aCfg['slack']['icon']) ? $this->_aCfg['slack']['icon'] : false); + + // returns bool or string with response from Slack API return $shkr->sendMessage($this->_sMessage, $sUser, $sIcon); } return false; @@ -77,13 +87,14 @@ class messenger { /** * send a message to all targets * @param string $sMessage + * @return void */ - public function sendMessage($sMessage) { - $this->_sMessage=$sMessage; + public function sendMessage(string $sMessage): void + { + $this->_sMessage = $sMessage; // echo '<pre>'.print_r($this->_aCfg, 1).'</pre>'.$sMessage.'<br>'; $this->_sendEmail(); $this->_sendToSlack(); - } } diff --git a/public_html/deployment/classes/page.class.php b/public_html/deployment/classes/page.class.php index 18e750c124900b2adefccd6f8018f19157768524..533d8c6b53544a35579f0bb50dc5a6874c4eac5a 100644 --- a/public_html/deployment/classes/page.class.php +++ b/public_html/deployment/classes/page.class.php @@ -1,52 +1,55 @@ <?php /** - * PIMPED APACHE-STATUS * Page class * Render output page by replacing placeholders * - * @package pimped_apache_status + * origin is from PIMPED APACHE-STATUS + * * @author Axel Hahn + * + * 2024-08-23 v1.1 Axel Hahn php8 only; added variable types; short array syntax */ -class Page { +class Page +{ /** - * output type of content - * @var array + * Output type of content + * @var string */ - private $sType = 'html'; + private string $sType = 'html'; /** - * array of strings for http response header + * Array of strings for http response header * @var array */ - private $aResponseHeader = array(); + private array $aResponseHeader = []; /** * Replacements in the template * @var array */ - private $aReplace = array( + private array $aReplace = [ '{{HEADER}}' => '', '{{CONTENT}}' => '', '{{FOOTER}}' => '', '{{JSONREADY}}' => '', - ); + ]; /** - * constructor (it does nothing) - * @return boolean (true) + * Constructor */ - public function __construct() { + public function __construct() + { $this->setOutputtype(); - return true; } /** - * wrap on document ready instructions in jQuery style - * @return type + * Wrap on document ready instructions in jQuery style + * @return string */ - private function _finalizeJsOnReady() { + private function _finalizeJsOnReady(): string + { return $this->aReplace["{{JSONREADY}}"] = ' <script> $(document).ready(function() { @@ -60,43 +63,48 @@ class Page { // ---------------------------------------------------------------------- /** - * get current page content - * @return string + * Get current page content + * @return string HTML code */ - public function getContent() { + public function getContent(): string + { return $this->aReplace['{{CONTENT}}']; } - + /** - * get current footer - * @return type + * Get current footer + * @return string */ - public function getFooter() { + public function getFooter(): string + { return $this->aReplace['{{FOOTER}}']; } /** - * get current header in response body - * @return type + * Get current header in response body + * @return string */ - public function getHeader() { + public function getHeader(): string + { return $this->aReplace['{{HEADER}}']; } /** - * get on ready javascript instructions - * @return type¨ + * Get on ready javascript instructions + * @return string */ - public function getJsOnReady() { + public function getJsOnReady(): string + { return $this->aReplace['{{JSONREADY}}']; } - - + + /** - * get output type + * Get output type * @return string */ - public function getOutputtype() { + public function getOutputtype() + { return $this->sType; } @@ -105,12 +113,14 @@ class Page { // ---------------------------------------------------------------------- /** - * add javascript for on ready execution + * Add javascript for on ready execution * @param string $s javascript code - * @return boolean + * @return bool */ - public function addJsOnReady($s) { - return $this->aReplace['{{JSONREADY}}'] .= $s; + public function addJsOnReady(string $s): string + { + $this->aReplace['{{JSONREADY}}'] .= $s; + return true; } /** @@ -118,67 +128,78 @@ class Page { * @param string $s * @return boolean */ - public function addResponseHeader($s) { - return $this->aResponseHeader[] = $s; + public function addResponseHeader(string $s) + { + $this->aResponseHeader[] = $s; + return true; } /** - * set html body; it replaces old content + * Set html body; it replaces old content * @param string $s html code * @return boolean */ - public function setContent($s) { - return $this->aReplace['{{CONTENT}}'] = $s; + public function setContent(string $s): bool + { + $this->aReplace['{{CONTENT}}'] = $s; + return true; } /** - * set footer in html body; it replaces old content + * Set footer in html body; it replaces old content * @param string $s html code * @return boolean */ - public function setFooter($s) { - return $this->aReplace['{{FOOTER}}'] = $s; + public function setFooter(string $s): bool + { + $this->aReplace['{{FOOTER}}'] = $s; + return true; } /** - * set html header; it replaces old content + * Set html header; it replaces old content * @param string $s html code * @return boolean */ - public function setHeader($s) { - return $this->aReplace['{{HEADER}}'] = $s; + public function setHeader(string $s): bool + { + $this->aReplace['{{HEADER}}'] = $s; + return true; } /** - * set javascript code on ready; it replaces old content + * Set javascript code on ready; it replaces old content * @param string $s javascript code * @return boolean */ - public function setJsOnReady($s) { - return $this->aReplace['{{JSONREADY}}'] = $s; + public function setJsOnReady(string $s): bool + { + $this->aReplace['{{JSONREADY}}'] = $s; + return true; } - + /** - * set output type of response + * Set output type of response * @param string $sOutputType * @return boolean */ - public function setOutputtype($sOutputType = 'html') { - return $this->sType = $sOutputType; + public function setOutputtype(string $sOutputType = 'html'): bool + { + $this->sType = $sOutputType; + return true; } - - // ---------------------------------------------------------------------- // OUTPUT // ---------------------------------------------------------------------- /** - * send http reponse headers and built the response body - * @return type + * Send http reponse headers and built the response body + * @return string */ - public function render() { - $aS = array(); // search - $aR = array(); // replace + public function render(): string + { + $aS = []; // search + $aR = []; // replace $this->_finalizeJsOnReady(); @@ -190,12 +211,12 @@ class Page { $sTemplate = false; $sTplFile = dirname(__FILE__) . "/" . $this->sType . ".tpl.php"; if (!file_exists($sTplFile)) { - die("ERROR: template for type " . $this->sType . " was not found: $sTplFile"); + throw new Exception("ERROR: template for type " . $this->sType . " was not found: $sTplFile"); } $sTemplate = file_get_contents($sTplFile); if (!$sTemplate) { - die("ERROR: template file $sTplFile is empty or could not be read."); + throw new Exception("ERROR: template file $sTplFile is empty or could not be read."); } foreach ($this->aResponseHeader as $sHeader) { @@ -205,5 +226,3 @@ class Page { } } - -?> diff --git a/public_html/deployment/classes/plugins.class.php b/public_html/deployment/classes/plugins.class.php index 9a348df6f55420aae619a8be2a5035b398a49c9a..43769dc579df9f9e739ba4c7d8c7feb3ebf51380 100644 --- a/public_html/deployment/classes/plugins.class.php +++ b/public_html/deployment/classes/plugins.class.php @@ -17,91 +17,94 @@ * * * @author axel + * + * 2024-08-26 v1.1 Axel Hahn php8 only; added variable types */ -class ciplugins { - +class ciplugins +{ + /** * start path of all plugin types (as subdirs) * @var string */ - protected $_sPlugindir=false; + protected string $_sPlugindir = ''; /** * path of the currently set plugin * @var string */ - protected $_sSelfdir=false; + protected string $_sSelfdir = ''; /** * url of set plugin * @var string */ - protected $_sSelfurl=false; + protected string $_sSelfurl = ''; /** * current plugin type - can be set via setType or setPlugin * @var string */ - protected $_sType=false; + protected string $_sType = ''; /** * current plugin name - can be set via setPlugin * @var string */ - protected $_sPluginname=false; + protected string $_sPluginname = ''; /** * plugin language * @var string */ - protected $_sLang = "en-en"; + protected string $_sLang = "en-en"; /** * plugin language texts (lang*.json) * @var array */ - protected $_aLang = []; + protected array $_aLang = []; /** * plugin configuration data (config.json) * @var array */ - protected $_aConfig = []; + protected array $_aConfig = []; /** * global plugins config * see config/config_custom.php - key plugins * @var array */ - protected $_aGlobals = []; + protected array $_aGlobals = []; // --------------------------------------------------------------- // CONSTRUCTOR // --------------------------------------------------------------- /** + * Constructor * initialize plugins * * @param array $aGlobals global settings for plugins * @return boolean */ - public function __construct($aGlobals=[]) { - - $this->_sPlugindir=dirname(__DIR__).'/plugins'; + public function __construct(array $aGlobals = []) + { + $this->_sPlugindir = dirname(__DIR__) . '/plugins'; $this->setGlobalCustoms($aGlobals); - - return true; } /** - * global configs + * Global configs * see config/config_custom.php - key plugins * * @param array $aGlobals global settings for plugins * @return boolean */ - public function setGlobalCustoms($aGlobals){ - $this->_aGlobals=$aGlobals; + public function setGlobalCustoms(array $aGlobals): bool + { + $this->_aGlobals = $aGlobals; return true; } // --------------------------------------------------------------- @@ -109,61 +112,65 @@ class ciplugins { // --------------------------------------------------------------- /** - * get an array of available plugin types read from filesystem + * Get an array of available plugin types read from filesystem * @return array */ - public function getPluginTypes(){ - $aReturn=[]; - foreach(glob($this->_sPlugindir.'/*', GLOB_ONLYDIR) as $sMydir){ - $aReturn[]=basename($sMydir); + public function getPluginTypes(): array + { + $aReturn = []; + foreach (glob($this->_sPlugindir . '/*', GLOB_ONLYDIR) as $sMydir) { + $aReturn[] = basename($sMydir); } return $aReturn; } /** - * get an array of available plugins read from filesystem + * Get an array of available plugins read from filesystem * * @param string $sType set a new type of plugin; default: use current type * @return array */ - public function getPlugins($sType=false){ - $aReturn=[]; - if($sType){ - if (!$this->setType($sType)){ + public function getPlugins(string $sType = ''): array + { + $aReturn = []; + if ($sType) { + if (!$this->setType($sType)) { return $aReturn; } } - foreach(glob($this->_sPlugindir.'/'.$this->_sType.'/*', GLOB_ONLYDIR) as $sMydir){ - $aReturn[]=basename($sMydir); + foreach (glob($this->_sPlugindir . '/' . $this->_sType . '/*', GLOB_ONLYDIR) as $sMydir) { + $aReturn[] = basename($sMydir); } return $aReturn; } /** - * get an array of enabled plugins + * Get an array of enabled plugins * config/config_custom.php - key "plugins" -> [type] -> [plugin] -> enabled * and it must be physically available * * @param string $sType set a new type of plugin; default: use current type * @return array */ - public function getEnabledPlugins($sType=false){ + public function getEnabledPlugins(string $sType = ''): array + { $aReturn = []; - if($sType){ - if (!$this->setType($sType)){ + if ($sType) { + if (!$this->setType($sType)) { return $aReturn; } } - if (isset($this->_aGlobals[$this->_sType]) + if ( + isset($this->_aGlobals[$this->_sType]) && is_array($this->_aGlobals[$this->_sType]) - ){ - foreach($this->_aGlobals[$this->_sType] as $sPluginName=>$aData){ + ) { + foreach ($this->_aGlobals[$this->_sType] as $sPluginName => $aData) { if ( - isset($aData['enabled']) + isset($aData['enabled']) && $aData['enabled'] - && is_dir($this->_sPlugindir.'/'.$this->_sType.'/'.$sPluginName) - ){ - $aReturn[]=$sPluginName; + && is_dir($this->_sPlugindir . '/' . $this->_sType . '/' . $sPluginName) + ) { + $aReturn[] = $sPluginName; } } } @@ -181,73 +188,80 @@ class ciplugins { // --------------------------------------------------------------- /** - * set a type for plugins ... what is a name of a subdir in the plugins directory - * @param {string} $sType Name of a plugin type, e.g. build|rollout + * Set a type for plugins ... what is a name of a subdir in the plugins directory + * @param string $sType Name of a plugin type, e.g. build|rollout + * @return bool */ - public function setType($sType){ - $this->_sType=false; - if(!$sType || !is_dir($this->_sPlugindir.'/'.$sType)){ + public function setType(string $sType): bool + { + $this->_sType = ''; + if (!$sType || !is_dir($this->_sPlugindir . '/' . $sType)) { return false; } - return $this->_sType=$sType; + $this->_sType = $sType; + return true; } /** - * reset vars before setting a new plugin; + * Reset vars before setting a new plugin; * called in testPlugin() * @return boolean */ - protected function _resetPluginData(){ - $this->_sPluginname=false; - $this->_sSelfdir=false; - $this->_sSelfurl=false; - $this->_aLang=[]; - $this->_aConfig=[]; + protected function _resetPluginData(): bool + { + $this->_sPluginname = false; + $this->_sSelfdir = false; + $this->_sSelfurl = false; + $this->_aLang = []; + $this->_aConfig = []; return true; } /** - * set a plugin without autoload of its php class + * Test if a plugin of given type exists + * * It returns the path of php class for true * or boolean false if it does not exist * * This can be used standalone to embed html code * without loading any php code of the plugin class. * - * @param {string} $sPluginName name of the plugin - * @param {string} $sType optional: set a type + * @param string $sPluginName name of the plugin + * @param string $sType optional: set a type * @return bool|string */ - public function testPlugin($sPluginName,$sType=false){ + public function testPlugin(string $sPluginName, string $sType = ''): bool|string + { $this->_resetPluginData(); - if($sType){ - if (!$this->setType($sType)){ + if ($sType) { + if (!$this->setType($sType)) { return false; } } - $this->_sSelfdir=$this->_sPlugindir.'/'.$this->_sType.'/'.$sPluginName; - $sFile=$this->_sSelfdir.'/plugin.php'; - if(!file_exists($sFile)){ + $this->_sSelfdir = $this->_sPlugindir . '/' . $this->_sType . '/' . $sPluginName; + $sFile = $this->_sSelfdir . '/plugin.php'; + if (!file_exists($sFile)) { // die(' MISS '.$sFile); - $this->_sSelfdir=false; + $this->_sSelfdir = false; return false; } - $this->_sPluginname=$sPluginName; - $this->_sSelfurl='/deployment/plugins/'.$this->_sType.'/'.$sPluginName; + $this->_sPluginname = $sPluginName; + $this->_sSelfurl = '/deployment/plugins/' . $this->_sType . '/' . $sPluginName; return $sFile; } /** - * set a plugin with autoload of its php class + * Set a plugin with autoload of its php class * It returns a boolean * - * @param {string} $sPluginName name of the plugin - * @param {string} $sType optional: set a type + * @param string $sPluginName name of the plugin + * @param string $sType optional: set a type * @return bool */ - public function setPlugin($sPluginName,$sType=false){ - $sFile=$this->testPlugin($sPluginName,$sType); - if(!$sFile){ + public function setPlugin(string $sPluginName, string $sType = ''): bool + { + $sFile = $this->testPlugin($sPluginName, $sType); + if (!$sFile) { return false; } include_once $sFile; @@ -257,25 +271,28 @@ class ciplugins { // getter for plugin // --------------------------------------------------------------- /** - * get config entry + * Get config entry of a plugin * @param string $sKey name of config value + * @return mixed */ - public function getConfigitem($sKey){ - return isset($this->_aConfig[$sKey]) ? $this->_aConfig[$sKey] : false; - } + public function getConfigitem(string $sKey): mixed + { + return $this->_aConfig[$sKey] ?? false; + } /** * get plugin config from its config.json * works with - * - shellcmd plugin + * - shellcmd plugins * @return array */ - public function getPluginConfig(){ - if(count($this->_aConfig)){ + public function getPluginConfig(): array + { + if (count($this->_aConfig)) { return $this->_aConfig; } - $this->_aConfig=(file_exists($this->_sSelfdir.'/config.json')) - ? json_decode(file_get_contents($this->_sSelfdir.'/config.json'), 1) - : ["error" => "config.json not found in ".$this->_sSelfdir] + $this->_aConfig = (file_exists($this->_sSelfdir . '/config.json')) + ? json_decode(file_get_contents($this->_sSelfdir . '/config.json'), 1) + : ["error" => "config.json not found in " . $this->_sSelfdir] ; return $this->_aConfig; } @@ -284,18 +301,12 @@ class ciplugins { // --------------------------------------------------------------- /** - * get a location of a plugin file with full path - * @param {bool} $bAutoload flag: autoload needed plugin file + * Get the classname of a plugin (it is generated by type and plugin name) * @return string */ - public function getPluginClassname(){ - return $this->_sType.'_'.$this->_sPluginname; - } - - public function initPlugin__unused(){ - $sClassname=$this->_sType.'_'.$this->_sPlugindir; - $TmpRolloutPlugin = new $sClassname([]); - + public function getPluginClassname(): string + { + return $this->_sType . '_' . $this->_sPluginname; } } diff --git a/public_html/deployment/classes/plugins_renderer.class.php b/public_html/deployment/classes/plugins_renderer.class.php index cd569f54b67f44215f6c6f81f660b03f568e5990..ff8c183374172a028b1ad43b27d81c5464470274 100644 --- a/public_html/deployment/classes/plugins_renderer.class.php +++ b/public_html/deployment/classes/plugins_renderer.class.php @@ -17,9 +17,12 @@ require_once('plugins.class.php'); * * * @author axel + * + * 2024-08-23 v1.1 Axel Hahn php8 only; added variable types */ -class plugin_renderer extends ciplugins { - +class plugin_renderer extends ciplugins +{ + // --------------------------------------------------------------- // // BELOW ARE METHODS FOR A SET SPECIFIC PLUGIN AND TYPE @@ -30,38 +33,41 @@ class plugin_renderer extends ciplugins { // --------------------------------------------------------------- // LANGUAGE TEXTS (needed in ui only) // --------------------------------------------------------------- - + /** - * get a translated text from lang_XX.json in plugin dir; + * Get a translated text from lang_XX.json in plugin dir; * If the key is missed it returns "[KEY :: LANG]" * * @see setLang() * @param string $sKey key to find in lang file * @return string */ - protected function _t($sKey){ + protected function _t(string $sKey): string + { return (isset($this->_aLang[$sKey]) && $this->_aLang[$sKey]) - ? $this->_aLang[$sKey] - : "[ $sKey :: $this->_sLang ]" + ? $this->_aLang[$sKey] + : "[ $sKey :: $this->_sLang ]" ; } /** - * set language for output of formdata and other texts. + * Set language for output of formdata and other texts. * This method loads the language file into a hash. The output of * translated texts can be done with $this->_t("your_key") * * @see _t() + * * @param string $sLang language code, i.e. "de" * @return boolean */ - public function setLang($sLang=false){ - $this->_sLang=$sLang ? $sLang : $this->_sLang; - - $oReflection=new ReflectionClass($this); - $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sLang.'.json'; - $this->_aLang=(file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang; - return true; + public function setLang(string $sLang = ''): bool + { + $this->_sLang = $sLang ?: $this->_sLang; + + $oReflection = new ReflectionClass($this); + $sFile = dirname($oReflection->getFileName()) . '/lang_' . $this->_sLang . '.json'; + $this->_aLang = (file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang; + return !!count($this->_aLang); } // --------------------------------------------------------------- // SETTER @@ -73,44 +79,52 @@ class plugin_renderer extends ciplugins { // --------------------------------------------------------------- /** - * for shellcmd plugins: get html code to load javascript file + * For shellcmd plugins: get html code to load javascript file * The file must exist in the plugin directory - * @params string $sFile (basename of) filename, eg. render.js + * + * @param string $sFile (basename of) filename, eg. render.js + * @return string */ - public function getHtmlLoadScript($sFile){ - return (file_exists($this->_sSelfdir.'/'.$sFile)) - ? '<script src="'.$this->_sSelfurl.'/'.$sFile.'"></script>'."\n" + public function getHtmlLoadScript(string $sFile): string + { + return (file_exists($this->_sSelfdir . '/' . $sFile)) + ? '<script src="' . $this->_sSelfurl . '/' . $sFile . '"></script>' . "\n" : '' ; } /** - * get id for an output div + * Get id for an output div * @return string */ - public function getHtmlOutId(){ - return $this->_sPluginname ? 'divPlugin'.$this->_sType.''.$this->_sPluginname : false; + public function getHtmlOutId(): string + { + return $this->_sPluginname ? 'divPlugin' . $this->_sType . '' . $this->_sPluginname : false; } + /** - * get id for the wrapper div of an output div + * Get id for the wrapper div of an output div * @return string */ - public function getHtmlOutIdWrapper(){ - return $this->getHtmlOutId().'Wrapper'; + public function getHtmlOutIdWrapper(): string + { + return $this->getHtmlOutId() . 'Wrapper'; } /** - * get html code for a shellcmd output window + * Get html code for a shellcmd output window + * @return string The HTML code */ - public function getHtmlOutwindow(){ - $aConfig=$this->getPluginConfig(); - return '<div id="'.$this->getHtmlOutIdWrapper().'" class="cmdoutbox">' - .'<div id="'.$this->getHtmlOutId().'" ' - .'class="out' - .(isset($aConfig['window-cols']) && $aConfig['window-cols'] ? ' cmd-cols-'.$aConfig['window-cols'] : '' ) - .(isset($aConfig['window-lines']) && $aConfig['window-lines'] ? ' cmd-lines-'.$aConfig['window-lines'] : '' ) - .'"></div>' - .'</div>'; + public function getHtmlOutwindow(): string + { + $aConfig = $this->getPluginConfig(); + return '<div id="' . $this->getHtmlOutIdWrapper() . '" class="cmdoutbox">' + . '<div id="' . $this->getHtmlOutId() . '" ' + . 'class="out' + . (isset($aConfig['window-cols']) && $aConfig['window-cols'] ? ' cmd-cols-' . $aConfig['window-cols'] : '') + . (isset($aConfig['window-lines']) && $aConfig['window-lines'] ? ' cmd-lines-' . $aConfig['window-lines'] : '') + . '"></div>' + . '</div>'; } // --------------------------------------------------------------- diff --git a/public_html/deployment/classes/plugins_shellcmd_request.class.php b/public_html/deployment/classes/plugins_shellcmd_request.class.php index a99ddd34b9d0d834fd601157df73ec1db55ed96c..805378fd732a7bf7f00a34d33763da24c2e9fdc1 100644 --- a/public_html/deployment/classes/plugins_shellcmd_request.class.php +++ b/public_html/deployment/classes/plugins_shellcmd_request.class.php @@ -5,37 +5,67 @@ * * Used in ../plugins/shellcmd/getdata.php * + * 2024-08-23 v1.1 Axel Hahn php8 only; added variable types */ require_once('plugins.class.php'); -class req_shellcmd { +class req_shellcmd +{ - protected $_sPlugin=false; - protected $_oPlugin=false; - protected $_aReturn=false; + /** + * plugin name + * @var string + */ + protected string $_sPlugin = ''; - protected $_aPluginConfig=[]; + /** + * pligin object + * @var object + */ + protected object $_oPlugin; - protected $_aResult=[]; - protected $_debug=false; + /** + * Array of return items + * TODO: might be removed + * @var array + protected array $_aReturn = []; + */ /** - * plugins class instance + * Configuration for the plugin + * @var array + */ + protected array $_aPluginConfig = []; + + /** + * Result after execution + * @var array + */ + protected array $_aResult = []; + + /** + * Flag: enable debug? Default: false + * @var bool + */ + protected bool $_debug = false; + + /** + * ciplugins class instance * @var object */ - protected $CI_plugins=false; + protected $CI_plugins = false; // --------------------------------------------------------------- // CONSTRUCTOR // --------------------------------------------------------------- /** - * constructor - * @return bool + * Constructor */ - public function __construct(){ - return $this->detectPlugin(); + public function __construct() + { + $this->detectPlugin(); } // --------------------------------------------------------------- @@ -43,39 +73,45 @@ class req_shellcmd { // --------------------------------------------------------------- /** - * helper execute a given command and return array with executed + * Helper: execute a given command and return array with executed * command, returncode, output + * @param string $sCmd command to execute * @return */ - protected function _execCommand($sCmd){ + protected function _execCommand(string $sCmd): array + { exec("$sCmd", $aOut, $iResult); return [ - 'command'=>$sCmd, - 'exitcode'=>$iResult, - 'time'=>date("H:i:s"), - 'output'=>$aOut, + 'command' => $sCmd, + 'exitcode' => $iResult, + 'time' => date("H:i:s"), + 'output' => $aOut, ]; } /** - * initialize the shellcmd plugin + * Initialize the shellcmd plugin * @returm boolean */ - protected function _loadPlugin(){ - $this->CI_plugins=new ciplugins(); + protected function _loadPlugin(): bool + { + $this->CI_plugins = new ciplugins(); $this->CI_plugins->setPlugin($this->_sPlugin, 'shellcmd'); - $sPluginclass=$this->CI_plugins->getPluginClassname(); - $this->_oPlugin=new $sPluginclass(); + $sPluginclass = $this->CI_plugins->getPluginClassname(); + $this->_oPlugin = new $sPluginclass(); + return true; } /** - * write debug output ... if enabled + * Write debug output ... if enabled + * @return void */ - protected function _wd($s){ - echo $this->_debug ? 'DEBUG '.__CLASS__ . ' '.$s."<br>\n" : ''; + protected function _wd($s): void + { + echo $this->_debug ? 'DEBUG ' . __CLASS__ . ' ' . $s . "<br>\n" : ''; } // --------------------------------------------------------------- @@ -83,40 +119,46 @@ class req_shellcmd { // --------------------------------------------------------------- /** - * detect plugin name to load from GET param "plugin" + * Detect plugin name to load from GET param "plugin" + * @return bool */ - public function detectPlugin(){ - $this->_sPlugin=isset($_GET['plugin']) && $_GET['plugin'] ? preg_replace('/^a-z0-9/', '', $_GET['plugin']) : false; - $this->_wd("detected plugin: ".$this->_sPlugin); + public function detectPlugin(): bool + { + $this->_sPlugin = isset($_GET['plugin']) && $_GET['plugin'] ? preg_replace('/^a-z0-9/', '', $_GET['plugin']) : false; + $this->_wd("detected plugin: " . $this->_sPlugin); return true; } - /** - * get data from plugin command and return array with executed + * Get data from plugin command and return array with executed * command, returncode, output, parsed data * @return array */ - public function get(){ + public function get(): array + { $this->_loadPlugin(); - if (!$this->_oPlugin){ - return $this->_aReturn; + if (!$this->_oPlugin) { + // return $this->_aReturn; + return []; } $this->CI_plugins->getPluginConfig(); - $sCmd=$this->CI_plugins->getConfigitem('command'); + $sCmd = $this->CI_plugins->getConfigitem('command'); $this->_wd("sCmd=$sCmd"); - - $this->_aResult=$this->_execCommand($sCmd); - if (method_exists($this->_oPlugin, "parsedata")){ - $this->_aResult=$this->_oPlugin->parsedata($this->_aResult); + + $this->_aResult = $this->_execCommand($sCmd); + if (method_exists($this->_oPlugin, "parsedata")) { + $this->_aResult = $this->_oPlugin->parsedata($this->_aResult); } return $this->_aResult; } /** - * send response as json + * Send response as json. + * It sends http response header and json data in body + * @return void */ - public function sendResponse(){ + public function sendResponse(): void + { header('Content-Type: application/json'); echo json_encode($this->get(), JSON_PRETTY_PRINT); } diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php index a1bce66746de6e8ce22a9e01d8f20ab836597d16..9c06d48bdcabeb419c759045f96d44acc12766a3 100644 --- a/public_html/deployment/classes/project.class.php +++ b/public_html/deployment/classes/project.class.php @@ -25,6 +25,8 @@ require_once 'htmlguielements.class.php'; --------------------------------------------------------------------- 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + (...) + 2024-08-28 Axel php8 only; added variable types; short array syntax ###################################################################### */ /** @@ -41,80 +43,84 @@ class project extends base * configuration ($aConfig in the config file) * @var array */ - protected $_aConfig = array(); + protected array $_aConfig = []; /** * configuration of the project (= $aProjects[ID] in the config file) * @var array */ - protected $_aPrjConfig = array(); + protected array $_aPrjConfig = []; /** * version infos of all phases * @var array */ - protected $_aData = array(); + protected array $_aData = []; /** * existing versions in the archive dir * @var array */ - protected $_aVersions = array(); + protected array $_aVersions = []; /** * output file to fetch processing content with ajax request * @var string */ - protected $_sProcessTempOut = false; + protected string $_sProcessTempOut = ''; /** * places of version infos in each deployment phase * @var array */ - protected $_aPlaces = array( + protected array $_aPlaces = [ "onhold" => "Queue", "ready2install" => "Puppet", "deployed" => "Installiert", - ); + ]; /** * collector for returncodes of multiple exec calls * @var int */ - protected $_iRcAll = 0; + protected int $_iRcAll = 0; /** * reference to html renderer class to draw output items * @var object */ - protected $_oHtml = false; + protected object $_oHtml; /** * object to access a version control, .e. git * @var object */ - protected $_oVcs = false; + protected object $_oVcs; /** * object for rollout - * @var type + * @var object */ - public $oRolloutPlugin = false; + public object $oRolloutPlugin; - protected $_sBranchname = false; + /** + * Name of the current branch + * @var string + */ + protected string $_sBranchname = ''; /** - * send messages - * @var messengerobject + * messenger object to send messages + * @var object */ - protected $oMessenger = false; + protected object $oMessenger; /** * collected errors * @var array */ - protected $_errors = []; + protected array $_errors = []; // ---------------------------------------------------------------------- // constructor @@ -124,7 +130,7 @@ class project extends base * constructor * @param string $sId id of the project */ - public function __construct($sId = false) + public function __construct(string $sId = '') { $this->oUser = new user(); $this->_oHtml = new htmlguielements(); @@ -144,7 +150,7 @@ class project extends base * @param string $sLevel warnlevel of the given message * @return bool */ - protected function log($sMessage, $sLevel = "info") + protected function log(string $sMessage, string $sLevel = "info") { global $oCLog; return $oCLog->add(basename(__FILE__) . " class " . __CLASS__ . " - " . $sMessage, $sLevel); @@ -157,26 +163,19 @@ class project extends base */ protected function _sendMessage($sMessage) { - $aConfig = array(); + $aConfig = []; - if ( - array_key_exists('messenger', $this->_aPrjConfig) - && array_key_exists('slack', $this->_aPrjConfig['messenger']) - ) { + if (isset($this->_aPrjConfig['messenger']['slack'])) { $sSlack = $this->_aPrjConfig['messenger']['slack']; - $aConfig['slack'] = array('incomingurl' => $sSlack); - foreach (array('user', 'icon') as $sKey) { + $aConfig['slack'] = ['incomingurl' => $sSlack]; + foreach (['user', 'icon'] as $sKey) { if (isset($this->_aConfig['messenger']['slack']['presets'][$sSlack][$sKey])) { $aConfig['slack'][$sKey] = $this->_aConfig['messenger']['slack']['presets'][$sSlack][$sKey]; } } } - if ( - array_key_exists('messenger', $this->_aPrjConfig) - && array_key_exists('email', $this->_aPrjConfig['messenger']) - && $this->_aPrjConfig['messenger']['email'] - ) { + if (isset($this->_aConfig['messenger']['email'])) { $aConfig['email'] = $this->_aConfig['messenger']['email']; $aConfig['email']['to'] = $this->_aPrjConfig['messenger']['email']; } @@ -185,7 +184,7 @@ class project extends base } // init on first usage - if (!$this->oMessenger) { + if (!isset($this->oMessenger) || !$this->oMessenger) { $this->oMessenger = new messenger($aConfig); } @@ -193,7 +192,7 @@ class project extends base $sText = $this->getLabel() . ': ' . html_entity_decode($sMessage) . "\n" . t('page-login-username') . ": " . $this->oUser->getUsername() . "\n"; if (isset($_SERVER) && is_array($_SERVER)) { - if (array_key_exists('HTTP_HOST', $_SERVER)) { + if (isset($_SERVER['HTTP_HOST'])) { $sText .= t('project-home') . ': ' . $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . '/deployment/' . $this->getId() . "\n"; } /* @@ -202,13 +201,14 @@ class project extends base } */ } - return $this->oMessenger->sendMessage($sText); + $this->oMessenger->sendMessage($sText); + return true; } /** * read default config file * @return boolean */ - protected function _readConfig() + protected function _readConfig(): bool { global $aConfig; $this->_aConfig = $aConfig; @@ -219,14 +219,14 @@ class project extends base * validate config data * @return boolean */ - protected function _verifyConfig() + protected function _verifyConfig(): bool { if (!is_array($this->_aPrjConfig) || !count($this->_aPrjConfig)) { // die(t("class-project-error-no-config")); throw new Exception(t("class-project-error-no-config")); } - if (!array_key_exists("packageDir", $this->_aConfig)) { + if (!isset($this->_aConfig["packageDir"])) { die(t("class-project-error-no-packagedir")); } if (!$this->_aConfig["packageDir"]) { @@ -235,7 +235,7 @@ class project extends base if (!file_exists($this->_aConfig["packageDir"])) { die(sprintf(t("class-project-error-packagedir-does-not-exist"), $this->_aConfig['packageDir'])); } - if (!array_key_exists("archiveDir", $this->_aConfig)) { + if (!isset($this->_aConfig["archiveDir"])) { die(t("class-project-error-no-archivedir")); } if (!$this->_aConfig["archiveDir"]) { @@ -245,8 +245,8 @@ class project extends base die(sprintf(t("class-project-error-packagedir-does-not-exist"), $this->_aConfig['archiveDir'])); } - foreach (array("fileprefix", "build", "phases") as $sKey) { - if (!array_key_exists($sKey, $this->_aPrjConfig)) { + foreach (["fileprefix", "build", "phases"] as $sKey) { + if (!isset($this->_aPrjConfig[$sKey])) { die(sprintf(t("class-project-error-missing-prjkey"), $sKey, print_r($this->_aPrjConfig, true))); } } @@ -260,7 +260,7 @@ class project extends base die(sprintf(t("class-project-error-data-does-not-exist"), $this->_aConfig['dataDir'])); } - foreach (array("database", "projects", "sshkeys") as $sKey) { + foreach (["database", "projects", "sshkeys"] as $sKey) { $sTestDir=$this->_aConfig["dataDir"]."/$sKey"; if (!file_exists($sTestDir)) { mkdir($sTestDir); @@ -273,10 +273,11 @@ class project extends base /** * execute a commandline; returns a string of output of timestamp, command, output and returncode - * @param string $sCommand + * @param string $sCommand command to execute + * @param bool $bFlush flush content of output buffer * @return string */ - protected function _execAndSend($sCommand, $bFlush = false) + protected function _execAndSend(string $sCommand, bool $bFlush = false): string { $this->log(__FUNCTION__ . " start"); $sReturn = ''; @@ -298,15 +299,15 @@ class project extends base $this->log(__FUNCTION__ . " ended command $sCommand"); $sReturn .= (count($aOutput)) ? htmlentities(implode("\n", $aOutput)) . "\n" : ""; /* - $descriptorspec = array( - 0 => array("pipe", "r"), // stdin is a pipe that the child will read from - 1 => array("pipe", "w"), // stdout is a pipe that the child will write to - 2 => array("pipe", "w") // stderr is a pipe that the child will write to - ); + $descriptorspec = [ + 0 => ["pipe", "r"], // stdin is a pipe that the child will read from + 1 => ["pipe", "w"], // stdout is a pipe that the child will write to + 2 => ["pipe", "w"] // stderr is a pipe that the child will write to + ]; if ($bFlush) { flush(); } - $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), array()); + $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), []); $sErrors = false; @@ -353,12 +354,13 @@ class project extends base } /** - * add an action log message - * @param string $sMessage message - * @param string $sAction project action - * @param string $sLoglevel loglevel + * add an action log message for the current project + * @param string $sMessage message text + * @param string $sAction project action i.e. build, deploy, ... + * @param string $sLoglevel loglevel; default: info + * @return void */ - protected function _logaction($sMessage, $sAction = "", $sLoglevel = "info") + protected function _logaction(string $sMessage, string $sAction = "", string $sLoglevel = "info"): void { require_once("actionlog.class.php"); $oLog = new Actionlog($this->_aConfig["id"]); @@ -369,7 +371,12 @@ class project extends base // GETTER // ---------------------------------------------------------------------- - protected function _getConfigFile($sId) + /** + * Get filename of fonfigfile for this project + * @param string $sId project id + * @return string + */ + protected function _getConfigFile(string $sId): string { if (!$sId) { die(t("class-project-error-_getConfigFile-requires-id")); @@ -378,49 +385,53 @@ class project extends base } /** - * get a full ath for temp directory (for a build) + * Get a full ath for temp directory (for a build) * @return string */ - protected function _getTempDir() + protected function _getTempDir(): string { return $s = $this->_getBuildDir() . '/' . $this->_aPrjConfig["fileprefix"] . "_" . date("Ymd-His"); } /** - * get full path where the project builds are (a build setes a subdir) + * Get full path where the project builds are (a build setes a subdir) * @return string */ - protected function _getBuildDir() + protected function _getBuildDir(): string { return $this->_aConfig['buildDir'] . '/' . $this->_aConfig["id"]; } /** - * get full path where the project default files are - * @return type + * Get full path where the project default files are + * This is an optional step in build() - you can sync default files into + * the build directory. It returns false if the directory with default + * files doesn't exist. + * + * @return string|bool */ - protected function _getDefaultsDir() + protected function _getDefaultsDir(): string|bool { $s = $this->_aConfig['buildDefaultsDir'] . '/' . $this->_aConfig["id"]; return file_exists($s) ? $s : false; } /** - * get directory for infofile and package (without extension) + * Get directory for infofile and package (without extension) + * * @param string $sPhase one of preview|stage|live ... * @param string $sPlace one of onhold|ready2install|deployed * @return string */ - protected function _getFileBase($sPhase, $sPlace) + protected function _getFileBase(string $sPhase, string $sPlace): string { - if (!array_key_exists($sPhase, $this->_aConfig["phases"])) { + if (!isset($this->_aConfig["phases"][$sPhase])) { die(sprintf(t("class-project-error-wrong-phase"), $sPhase)); } - if (!array_key_exists($sPlace, $this->_aPlaces)) { + if (!isset($this->_aPlaces[$sPlace])) { die(sprintf(t("class-project-error-wrong-place"), $sPlace)); } - // local file for onhold|ready2install $sBase = $this->_aConfig['packageDir'] . "/" . $sPhase . "/" . $this->_aPrjConfig["fileprefix"]; if (!file_exists($this->_aConfig['packageDir'] . "/" . $sPhase)) { @@ -433,7 +444,7 @@ class project extends base // $sBase .= "/" . $this->_aPrjConfig["fileprefix"]; // url for deployed if ($sPlace == "deployed") { - if ($this->isActivePhase($sPhase) && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) { + if ($this->isActivePhase($sPhase) && isset($this->_aPrjConfig["phases"][$sPhase]["url"])) { // $sBase = $this->_aPrjConfig["phases"][$sPhase]["url"] . $this->_aPrjConfig["fileprefix"]; $sBase = $this->_aPrjConfig["phases"][$sPhase]["url"]; } else { @@ -446,48 +457,53 @@ class project extends base } /** - * get filename for info/ meta file (.json file) + * Get filename for info/ meta file (.json file). + * It returns false if the base directory doesn't exist + * * @param string $sPhase one of preview|stage|live ... * @param string $sPlace one of onhold|ready2install|deployed * @return string */ - protected function _getInfofile($sPhase, $sPlace) + protected function _getInfofile(string $sPhase, string $sPlace): string|bool { $sBase = $this->_getFileBase($sPhase, $sPlace); return $sBase ? $sBase . '/' . $this->_aPrjConfig["fileprefix"] . '.json' : false; } /** - * get filename for package file (without file extension) + * Get filename for package file (without file extension) + * It returns false if the base directory doesn't exist + * * @param string $sPhase one of preview|stage|live ... * @param string $sPlace one of onhold|ready2install|deployed * @return string */ - protected function _getPackagefile($sPhase, $sPlace) + protected function _getPackagefile(string $sPhase, string $sPlace) { $sBase = $this->_getFileBase($sPhase, $sPlace); return $sBase ? $sBase . '/' . $this->_aPrjConfig["fileprefix"] : false; } /** - * list of files of a given phase and place - * @param string $sPhase one of preview|stage|live ... - * @param string $sPlace one of onhold|ready2install|deployed - * @return array + * Get a list of files of a given phase and place. + * It returns false if the base directory doesn't exist + * + * @param string $sBase base directory from where to get files (archive dir of a build) + * @return bool|array */ - public function _getBuildfilesByDir($sBase) + public function _getBuildfilesByDir(string $sBase): bool|array { - $aReturn = array(); + $aReturn = []; if (!$sBase || !is_dir($sBase)) { return false; } $iTotalSize = 0; - $aReturn = array( + $aReturn = [ 'dir' => $sBase, 'filecount' => false, 'totalsize' => false, 'totalsize-hr' => false, - ); + ]; foreach (glob($sBase . '/*') as $sFile) { $sFileBase = basename($sFile); @@ -513,12 +529,12 @@ class project extends base break; } $iTotalSize += $aStat['size']; - $aReturn['files'][$sFileBase] = array( + $aReturn['files'][$sFileBase] = [ 'type' => $sType, 'icon' => $this->_oHtml->getIcon($sIcon), 'extension' => $sExt, 'size' => $aStat['size'], - ); + ]; $aReturn['types'][$sType][] = $sFileBase; } $aReturn['totalsize'] = $iTotalSize; @@ -529,51 +545,61 @@ class project extends base } /** - * list of files of a given phase and place + * Get a list of files of a given phase and place; + * It returns false if the base directory for phase + base doesn't exist + * * @param string $sPhase one of preview|stage|live ... * @param string $sPlace one of onhold|ready2install|deployed - * @return array + * @return bool|array */ - public function getBuildfilesByPlace($sPhase, $sPlace) + public function getBuildfilesByPlace(string $sPhase, string $sPlace): bool|array { $sBase = $this->_getFileBase($sPhase, $sPlace); return $this->_getBuildfilesByDir($sBase); } /** - * list of files of a given version number + * Get a list of files of a given version number + * It returns false if the version directory doesn't exist + * * @param string $sVersion name of version * @return array */ - public function getBuildfilesByVersion($sVersion) + public function getBuildfilesByVersion(string $sVersion): bool|array { return $this->_getBuildfilesByDir($this->_getProjectArchiveDir() . '/' . $sVersion); } /** - * get the group id of the project - * @return string + * Get the group id of the project + * It returns false if the group wasn't set + * + * @return bool|string */ - public function getProjectGroup() + public function getProjectGroup(): bool|string { return isset($this->_aPrjConfig["projectgroup"]) && $this->_aPrjConfig["projectgroup"] != '-1' ? $this->_aPrjConfig["projectgroup"] : false; } + /** - * get the group label of the project - * @return string + * Get the group label (description) of the project + * It returns false if the group wasn't set + * + * @return bool|string */ - public function getProjectGroupLabel() + public function getProjectGroupLabel(): bool|string { $sGroupid = $this->getProjectGroup(); return isset($this->_aConfig["projectgroups"][$sGroupid]) ? $this->_aConfig["projectgroups"][$sGroupid] : false; } /** - * get full path of a packed project archive + * Get full path of a packed project archive + * * @param string $sVersion version number of the build * @return string */ - protected function _getArchiveDir($sVersion) + protected function _getArchiveDir(string $sVersion): string { if (!$sVersion) { die(t("class-project-error-_getArchiveDir-requires-id")); @@ -586,10 +612,11 @@ class project extends base * - key "ok" anddata * or * - key "error" with the message - * @param type $sTimestamp + * + * @param string $sTimestamp * @return array */ - protected function _getArchiveInfos($sTimestamp) + protected function _getArchiveInfos(string $sTimestamp): array { if (!$sTimestamp) { die(t("class-project-error-_getArchiveInfos-requires-id")); @@ -603,7 +630,7 @@ class project extends base return $aReturn; } $aJson = json_decode(file_get_contents($sInfoFile), true); - if (is_array($aJson) && array_key_exists("version", $aJson)) { + if (is_array($aJson) && isset($aJson["version"])) { $aReturn = array_merge($aReturn, $aJson); $aReturn['ok'] = 1; /* @@ -621,48 +648,26 @@ class project extends base } /** - * get the directory for archive files of this project + * Get the directory for archive files of this project + * * @return string */ - public function _getProjectArchiveDir() + public function _getProjectArchiveDir(): string { return $this->_aConfig["archiveDir"] . '/' . $this->_aConfig["id"]; } /** - * TODO: REMOVE - * make an http get request and return the response body - * @param string $url - * @return string - */ - private function _httpGet($url, $iTimeout = 5) - { - $this->log(__FUNCTION__ . " start"); - if (!function_exists("curl_init")) { - die("ERROR: PHP CURL module is not installed."); - } - $this->log(__FUNCTION__ . " url: $url"); - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); - curl_setopt($ch, CURLOPT_TIMEOUT, $iTimeout); - curl_setopt($ch, CURLOPT_USERAGENT, 'IML Deployment GUI'); - $res = curl_exec($ch); - curl_close($ch); - $this->log(__FUNCTION__ . " done url: $url"); - return $res; - } - - /** - * get all existing versions in archive and its usage + * Get all existing versions in archive and its usage * versions are array keys; where they are used is written in values + * * @return array */ - public function getVersions() + public function getVersions(): array { // --- read all file entries - $aReturn = array(); + $aReturn = []; $sDir = $this->_getProjectArchiveDir(); if (is_dir($sDir)) { foreach (scandir($sDir) as $sEntry) { @@ -677,21 +682,23 @@ class project extends base foreach ($this->_aData["phases"] as $sPhase => $aData) { foreach (array_keys($this->_aPlaces) as $sPlace) { if (isset($aData[$sPlace]["version"])) { - $this->_aVersions[$aData[$sPlace]["version"]][] = array('phase' => $sPhase, 'place' => $sPlace); + $this->_aVersions[$aData[$sPlace]["version"]][] = ['phase' => $sPhase, 'place' => $sPlace]; } } } ksort($this->_aVersions); return $this->_aVersions; } + /** - * get an array with all existing build error output files (html) + * Get an array with all existing build error output files (html) + * * @return array */ - public function getBuildErrors($sProject = false) + public function getBuildErrors(string $sProject = ''): array { // --- read all file entries - $aReturn = array(); + $aReturn = []; if (!$sProject) { $sProject = $this->_aPrjConfig["fileprefix"] . '_*'; } @@ -700,11 +707,14 @@ class project extends base } return $aReturn; } + /** - * get an array with all existing build error output files (html) - * @return array + * Get an array with all existing build error output files (html) + * It returns false when path of given logfile contains ".." or the logfile doesn't exist. + * + * @return bool|string */ - public function getBuildErrorContent($sLogfile) + public function getBuildErrorContent($sLogfile): bool|string { if (!strpos('..', $sLogfile) === false) { return false; @@ -717,16 +727,17 @@ class project extends base } /** - * get Array of all versions, metainfos, in which phases they are in use + * Get an array of all versions, metainfos, in which phases they are in use * and a rollback ist possible or not + * * return array */ - protected function _getVersionUsage() + protected function _getVersionUsage(): array { - $aVersionData = array(); + $aVersionData = []; $sLastVersion = false; if (!count($this->getVersions())) { - return array(); + return []; } foreach ($this->getVersions() as $sVersion => $aData) { @@ -755,7 +766,7 @@ class project extends base $bCanRollback = false; } /* - if (!array_key_exists("ok", $aVersionData[$sVersion]["info"])){ + if (!isset($aVersionData[$sVersion]["info"]["ok"])){ $bCanRollback = false; } */ @@ -769,11 +780,12 @@ class project extends base } /** - * recursive delete + * Recursive delete of a given directory + * * @param string $dir directory to delete - * @return type + * @return bool */ - protected function _rmdir($dir) + protected function _rmdir(string $dir): bool { foreach (scandir($dir) as $sEntry) { if (is_dir($dir . '/' . $sEntry) && $sEntry != '.' && $sEntry != '..') { @@ -785,17 +797,21 @@ class project extends base } /** - * cleanup of archive directory; it returns the list of deleted - * directories as array - * @return array + * Cleanup of archive directory; it returns the list of deleted + * directories as array. + * It returns a string with the error message in case of missing permission + * Otherwise it returns an array with all deleted directories. + * + * @param bool $bDeleteAll flag to delete all; default: false = it keeps a few versions + * @return string|array */ - public function cleanupArchive($bDeleteAll = false) + public function cleanupArchive(bool $bDeleteAll = false): string|array { if (!$this->oUser->hasPermission("project-action-cleanup")) { return $this->oUser->showDenied(); } - $aDelete = array(); - $aUnused = array(); + $aDelete = []; + $aUnused = []; $sDir = $this->_getProjectArchiveDir(); $this->getVersions(); @@ -837,19 +853,21 @@ class project extends base } /** - * cleanup of archive directory; it returns the list of deleted - * directories as array - * @return array + * Cleanup of kept build directories (builds with errors) except the last N builds; + * It returns a string with the error message in case of missing permission + * Otherwise it returns an array with all deleted directories. + * + * @return string|array */ - public function cleanupBuilds() + public function cleanupBuilds(): string|array { $this->log(__FUNCTION__ . " start"); if (!$this->oUser->hasPermission("project-action-cleanup")) { return $this->oUser->showDenied(); } $sDir = $this->_getBuildDir(); - $aDirlist = array(); - $aDelete = array(); + $aDirlist = []; + $aDelete = []; if (is_dir($sDir)) { foreach (scandir($sDir) as $sEntry) { if (is_dir($sDir . '/' . $sEntry) && $sEntry != '.' && $sEntry != '..') @@ -864,91 +882,102 @@ class project extends base if ($this->_rmdir($sDir2)) { $aDelete[] = $sDir2; } else { - echo t("class-project-warning-cannot-delete-build-dir", $sDir2); - }; + echo sprintf(t("class-project-warning-cannot-delete-build-dir"), $sDir2); + } + ; } return $aDelete; } /** - * cleanup cache of vcs - * @param type $iAge + * Cleanup cache of vcs + * It returns a string with error message in case of missing permission + * Otherwise it returns the success as true or false + * + * @param int $iAge max age in sec + * @return string|bool */ - public function cleanupVcsCache($iAge = 0) + public function cleanupVcsCache($iAge = 0): string|bool { $this->log(__FUNCTION__ . " start"); if (!$this->oUser->hasPermission("project-action-cleanup")) { return $this->oUser->showDenied(); } $this->_initVcs(); - if ($this->_oVcs) { + if (isset($this->_oVcs) && $this->_oVcs) { if (!method_exists($this->_oVcs, "cleanupCache")) { // the version control class does not have this method $this->log(__FUNCTION__ . " sorry, Method cleanupCache does not exist in this VCS class."); - return ''; + return false; } return $this->_oVcs->cleanupCache($iAge); } + return false; } /** - * get conmplete config of the project + * Get conmplete config of the project + * * @return array */ - public function getConfig() + public function getConfig(): array { return $this->_aPrjConfig; } /** - * get name/ label of the project + * Get name/ label of the project + * * @return string */ - public function getLabel() + public function getLabel(): string { return isset($this->_aPrjConfig["label"]) ? $this->_aPrjConfig["label"] : ''; } /** - * get description of the project + * Get description of the project + * * @return string */ - public function getDescription() + public function getDescription(): string { return isset($this->_aPrjConfig["description"]) ? $this->_aPrjConfig["description"] : ''; } /** - * get the id of the current project + * Get the id of the current project * @return string */ - public function getId() + public function getId(): string { return isset($this->_aConfig["id"]) ? $this->_aConfig["id"] : ''; } + /** - * get deploy and queue infos for all phases + * Get deploy and queue infos for all phases * It build up a subkey "progress" with info if a build is queued * or an installation of a new package is going on + * * @return array */ - public function getAllPhaseInfos() + public function getAllPhaseInfos(): array { $bHasQueue = false; $bHasDifferentVersions = false; $bFirstVersion = false; - if (!array_key_exists("phases", $this->_aData)) { - $this->_aData["phases"] = array(); + if (!isset($this->_aData["phases"])) { + $this->_aData["phases"] = []; } - if (!array_key_exists("progress", $this->_aData)) { - $this->_aData["progress"] = array(); + if (!isset($this->_aData["progress"])) { + $this->_aData["progress"] = []; } foreach (array_keys($this->_aConfig["phases"]) as $sPhase) { - if (!array_key_exists($sPhase, $this->_aData["phases"])) { + if (!isset($this->_aData["phases"][$sPhase])) { $this->getPhaseInfos($sPhase); } // detect progress @@ -971,39 +1000,40 @@ class project extends base $bHasQueue = true; } } - $this->_aData["progress"] = array( + $this->_aData["progress"] = [ 'inprogress' => $bHasDifferentVersions, 'hasQueue' => $bHasQueue, - ); + ]; return $this->_aData["phases"]; } /** - * get statusinfos of a named phase + * Get statusinfos of a named phase + * * @param string $sPhase name of the phase; one of preview|stage|live * @return array */ - public function getPhaseInfos($sPhase) + public function getPhaseInfos(string $sPhase): array { if (!$sPhase) { die(t("class-project-error-getPhaseInfos-requires-phase")); } - if (!array_key_exists("phases", $this->_aData)) - $this->_aData["phases"] = array(); + if (!isset($this->_aData["phases"])) + $this->_aData["phases"] = []; - if (!array_key_exists($sPhase, $this->_aData["phases"])) { + if (!isset($this->_aData["phases"][$sPhase])) { if ($this->isActivePhase($sPhase)) { - $this->_aData["phases"][$sPhase] = array(); - $aTmp = array(); + $this->_aData["phases"][$sPhase] = []; + $aTmp = []; // a blocked package is waiting for deployment timeslot? $sKey = "onhold"; $sJsonfile = $this->_getInfofile($sPhase, $sKey); - $aTmp[$sKey] = array(); + $aTmp[$sKey] = []; if (file_exists($sJsonfile)) { $aJson = json_decode(file_get_contents($sJsonfile), true); - if (array_key_exists("version", $aJson)) { + if (isset($aJson["version"])) { $aTmp[$sKey] = $aJson; $aTmp[$sKey]["infofile"] = $sJsonfile; $aTmp[$sKey]["ok"] = 1; @@ -1018,12 +1048,12 @@ class project extends base // package for puppet $sKey = "ready2install"; $sJsonfile = $this->_getInfofile($sPhase, $sKey); - $aTmp[$sKey] = array(); + $aTmp[$sKey] = []; if (file_exists($sJsonfile)) { // $sPkgfile = $this->_getPackagefile($sPhase, $sKey); // if (file_exists($sPkgfile)) { $aJson = json_decode(file_get_contents($sJsonfile), true); - if (is_array($aJson) && array_key_exists("version", $aJson)) { + if (isset($aJson["version"])) { $aTmp[$sKey] = $aJson; $aTmp[$sKey]["infofile"] = $sJsonfile; // $aTmp[$sKey]["packagefile"] = $sPkgfile; @@ -1041,7 +1071,7 @@ class project extends base // published data $sKey = "deployed"; $sJsonfile = $this->_getInfofile($sPhase, $sKey); - $aTmp[$sKey] = array(); + $aTmp[$sKey] = []; // use version cache require_once(__DIR__ . '/../../valuestore/classes/valuestore.class.php'); @@ -1051,11 +1081,11 @@ class project extends base // echo "Place: <pre>" . print_r($oVersion->whereiam(), 1) . "</pre>"; // echo "Versionen: <pre>" . print_r($aVersions, 1) . "</pre>"; if (count($aVersions)) { - $aTmp[$sKey] = array(); + $aTmp[$sKey] = []; $aTmp[$sKey] = $aVersions[0]['_data']; $aTmp[$sKey]["infofile"] = '[versioncache]'; - $aTmp[$sKey]['_hosts'] = array(); + $aTmp[$sKey]['_hosts'] = []; foreach ($aVersions as $sHostname => $aHostdata) { $aTmp[$sKey]['_hosts'][$aHostdata['host']] = $aHostdata; } @@ -1099,12 +1129,13 @@ class project extends base * </code> * returns<br> * Array ( [0] => project1 [1] => project2 ) + * * @param string $sort sort by "id" (default) or "label" * @return array */ - public function getProjects($sort = 'id') + public function getProjects(string $sort = 'id'): array { - $aReturn = array(); + $aReturn = []; foreach (glob(dirname($this->_getConfigFile("dummy")) . "/*.json") as $filename) { $aReturn[] = str_replace(".json", "", basename($filename)); } @@ -1128,11 +1159,12 @@ class project extends base } /** - * check if the given phase is active for this project - * @param type $sPhase - * @return type + * Check if the given phase is active for this project + * + * @param string $sPhase name of the phase; one of preview|stage|live + * @return bool */ - public function isActivePhase($sPhase) + public function isActivePhase(string $sPhase): bool { return ( $this->_aPrjConfig && isset($this->_aPrjConfig["phases"][$sPhase]["active"][0]) @@ -1142,29 +1174,29 @@ class project extends base } /** - * return array of all (active and inactive) phases + * Get array of all (active and inactive) phases * @return array */ - public function getPhases() + public function getPhases(): array { return $this->_aConfig["phases"]; } /** - * return array of all (active and inactive) phases + * Get array of all (active and inactive) phases * @return array */ - public function getPlaces() + public function getPlaces(): array { return $this->_aPlaces; } /** - * get a flat array with active phases of the project + * Get a flat array with active phases of the project * @return array */ - public function getActivePhases() + public function getActivePhases(): array { - $aReturn = array(); + $aReturn = []; foreach (array_keys($this->_aConfig["phases"]) as $s) { if ($this->isActivePhase($s)) { $aReturn[] = $s; @@ -1176,11 +1208,12 @@ class project extends base /** * find the next active phase of a project * @param string $sPhase current phase; if empty the function sends back the first phase + * @return string */ - public function getNextPhase($sPhase = false) + public function getNextPhase(string $sPhase = ''): string { if ($sPhase) { - if (!array_key_exists($sPhase, $this->_aConfig["phases"])) { + if (!isset($this->_aConfig["phases"][$sPhase])) { die(sprintf(t("class-project-error-wrong-phase"), $sPhase)); } } @@ -1204,21 +1237,25 @@ class project extends base } /** - * get an array with deploy status ... + * Get an array with deploy status ... * 'inprogress'=>do versions differ from phase to phase = rollout of a version is in progress 'hasQueue'=>is there a package in a queue (waiting for deployment time to get ready to be installed) * @return array */ - public function getProgress() + public function getProgress(): array { $this->getAllPhaseInfos(); return $this->_aData['progress']; } /** * check: is the deployment to the next phase enabled for this phase? - * @param type $sPhase current phase + * It returns a string when current user has no permissions. + * Otherwise it returns true or false. + * + * @param string $sPhase current phase + * @return string|bool */ - public function canAcceptPhase($sPhase = false) + public function canAcceptPhase(string $sPhase = ''): string|bool { if ( !$this->oUser->hasPermission("project-action-accept") && !$this->oUser->hasPermission("project-action-accept-$sPhase") @@ -1231,7 +1268,7 @@ class project extends base // for better performance: skip check on overview page /* $aRepodata = $this->getRepoRevision(); - if (!array_key_exists("revision", $aRepodata)) { + if (!isset($aRepodata["revision"])) { return false; } */ @@ -1240,7 +1277,7 @@ class project extends base } - if (!array_key_exists($sPhase, $this->_aConfig["phases"])) { + if (!isset($this->_aConfig["phases"][$sPhase])) { die(sprintf(t("class-project-error-wrong-phase"), $sPhase)); } if (!$this->isActivePhase($sPhase)) { @@ -1258,7 +1295,14 @@ class project extends base // array key "ok" must be in the ready2install and deployed info // and a version must be installed if ( - array_key_exists($sPhase, $this->_aData["phases"]) && array_key_exists("onhold", $this->_aData["phases"][$sPhase]) && array_key_exists("ready2install", $this->_aData["phases"][$sPhase]) && array_key_exists("deployed", $this->_aData["phases"][$sPhase]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["onhold"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["ready2install"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["deployed"]) && array_key_exists("version", $this->_aData["phases"][$sPhase]["deployed"]) + isset($this->_aData["phases"][$sPhase]) + && isset($this->_aData["phases"][$sPhase]["onhold"]) + && isset($this->_aData["phases"][$sPhase]["ready2install"]) + && isset($this->_aData["phases"][$sPhase]["deployed"]) + && isset($this->_aData["phases"][$sPhase]["onhold"]["ok"]) + && isset($this->_aData["phases"][$sPhase]["ready2install"]["ok"]) + && isset($this->_aData["phases"][$sPhase]["deployed"]["ok"]) + && isset($this->_aData["phases"][$sPhase]["deployed"]["version"]) ) { return true; } @@ -1266,45 +1310,46 @@ class project extends base } /** - * get list of remote branches and tags + * Get list of remote branches and tags. + * It returns false if the VCS was not initialize or has no method getRemoteBranches() + * * @param bool $bIgnoreCache flag to ignore exiting cached data - * @return array|boolean + * @return bool|array */ - public function getRemoteBranches($bIgnoreCache = false) + public function getRemoteBranches(bool $bIgnoreCache = false): bool|array { $this->log(__FUNCTION__ . "($bIgnoreCache) start"); $this->_initVcs(); - if ($this->_oVcs) { + if (isset($this->_oVcs) && $this->_oVcs) { if (!method_exists($this->_oVcs, "getRemoteBranches")) { // the version control class does not have this method - return ''; + return false; } return $this->_oVcs->getRemoteBranches($bIgnoreCache); } return false; } - - /** - * get current revision and log message from remote repo + * Get current revision and log message from remote repo + * * @param boolean $bRefresh optional: refresh data; default: use cache * @return array */ - public function getRepoRevision($bRefresh = false) + public function getRepoRevision(bool $bRefresh = false): array { $this->log(__FUNCTION__ . "($bRefresh) start"); if (!$this->_aPrjConfig["build"]["type"]) { - $this->_aData["phases"]["source"] = array("error" => t("class-project-error-repo-type-not-set"),); + $this->_aData["phases"]["source"] = ["error" => t("class-project-error-repo-type-not-set"),]; } else { $this->_initVcs(); - if ($this->_oVcs) { + if (isset($this->_oVcs) && $this->_oVcs) { $this->_aData["phases"]["source"] = $this->_oVcs->getRepoRevision($bRefresh); } else { - $this->_aData["phases"]["source"] = array( + $this->_aData["phases"]["source"] = [ "error" => sprintf(t("class-project-error-repo-type-not-supported"), $this->_aPrjConfig["build"]["type"]), - ); + ]; } } $this->log(__FUNCTION__ . " result:<pre>" . print_r($this->_aData, 1) . "</pre>"); @@ -1312,20 +1357,20 @@ class project extends base } /** - * init version control system (git, ...) - * @return vcs-object + * Initialize version control system (git, ...) if it is not initialized yet + * it sets the object $this->_oVcs */ - protected function _initVcs() + protected function _initVcs(): void { $this->log(__FUNCTION__ . " start"); - if (!$this->_oVcs) { + if (!isset($this->_oVcs)) { if (!$this->_aPrjConfig["build"]["type"]) { - $this->_aData["phases"]["source"] = array("error" => t("class-project-error-repo-type-not-set"),); + $this->_aData["phases"]["source"] = ["error" => t("class-project-error-repo-type-not-set"),]; } else { if (!@include_once("vcs." . $this->_aPrjConfig["build"]["type"] . ".class.php")) { - $this->_aData["phases"]["source"] = array( + $this->_aData["phases"]["source"] = [ "error" => sprintf(t("class-project-error-repo-type-not-supported"), $this->_aPrjConfig["build"]["type"]), - ); + ]; } else { $aConfig = $this->_aPrjConfig["build"]; // for vcs classes @@ -1341,17 +1386,17 @@ class project extends base } } } - return $this->_oVcs; + // return $this->_oVcs; } /** - * get an array of enabled plugins + * Get an array of enabled plugins * @param string $sSection one of false|"rollout"|... * @return array */ - public function getConfiguredPlugins($sSection = false) + public function getConfiguredPlugins(string $sSection = ''): array { - $aReturn = array(); + $aReturn = []; if (!$sSection) { $aReturn = $this->_aConfig["plugins"]; } else { @@ -1363,23 +1408,24 @@ class project extends base } /** - * get a location of a plugin file with full path + * Get a location of a plugin file with full path + * * @param string $sType type of plugin, i.e. "rollout" * @param string $sPluginName Name of plugin * @return string */ - protected function _getPluginFilename($sType, $sPluginName) + protected function _getPluginFilename(string $sType, string $sPluginName): string { return __DIR__ . '/../plugins/' . $sType . '/' . $sPluginName . '/' . $sType . '_' . $sPluginName . '.php'; } /** - * get a flat array of all existing ssh keys + * Get a flat array of all existing ssh keys * @return array */ - protected function _getSshKeys() + protected function _getSshKeys(): array { - $aReturn = array(); + $aReturn = []; foreach (glob($this->_aConfig["dataDir"] . "/sshkeys/*.pub") as $filename) { $aReturn[] = str_replace(".pub", "", basename($filename)); } @@ -1388,25 +1434,27 @@ class project extends base } /** - * get a flat array with regexes of deploy times + * Get a flat array with regexes of deploy times. + * It returns false if the given phase is not active + * * @param string $sPhase phase - * @return array + * @return bool|array */ - protected function _getDeploytimes($sPhase) + protected function _getDeploytimes(string $sPhase): bool|array { if (!$this->isActivePhase($sPhase)) { $sError = sprintf(t("class-project-warning-phase-not-active"), $sPhase); $this->_logaction($sError, __FUNCTION__, "error"); return false; } - $aDeploytimes = array(); - if (array_key_exists("deploytimes", $this->_aConfig["phases"][$sPhase])) { - $aDeploytimes = $this->_aConfig["phases"][$sPhase]["deploytimes"]; - } + + $aDeploytimes = $this->_aConfig["phases"][$sPhase]["deploytimes"] ?? []; + if ( - array_key_exists("deploytimes", $this->_aPrjConfig["phases"][$sPhase]) && $this->_aPrjConfig["phases"][$sPhase]["deploytimes"] + isset($this->_aPrjConfig["phases"][$sPhase]["deploytimes"]) + && $this->_aPrjConfig["phases"][$sPhase]["deploytimes"] ) { - $aDeploytimes = array($this->_aPrjConfig["phases"][$sPhase]["deploytimes"]); + $aDeploytimes = [$this->_aPrjConfig["phases"][$sPhase]["deploytimes"]]; } return $aDeploytimes; } @@ -1415,7 +1463,16 @@ class project extends base // SETTER // ---------------------------------------------------------------------- - protected function _setProcessOutFile($sNewTempfile = false) + /** + * Generate a filename for the process output. + * The last output file will be deleted if it exists. + * It returns false if no pram for new basename was given. + * Otherwise it returns a filename with full path in temp folder. + * + * @param string $sNewTempfile + * @return bool|string + */ + protected function _setProcessOutFile(string $sNewTempfile = ''): bool|string { if ($this->_sProcessTempOut && file_exists($this->_sProcessTempOut)) { unlink($this->_sProcessTempOut); @@ -1426,21 +1483,23 @@ class project extends base } /** - * get projects from ldap; it returns ldap search items with cn as + * Get projects from ldap; it returns ldap search items with cn as * array key. + * It returns false if no result was found * - * @return array + * @param string $sSearchFilter LDAP search filter + * @return bool|array */ - protected function _ldapProjectSearch($sSearchFilter) + protected function _ldapProjectSearch(string $sSearchFilter): bool|array { - $aReturn = array(); + $aReturn = []; require_once("ldap.class.php"); $oLdapIML = new imlldap($this->_aConfig['projects']['ldap']); // $oLdapIML->debugOn(); $aResultsIml = $oLdapIML->searchDn( $this->_aConfig['projects']['ldap']['DnProjects'], $sSearchFilter, - array("*") + ["*"] ); if (!$aResultsIml['count']) { return false; @@ -1450,13 +1509,13 @@ class project extends base /* unset($aResultsIml['count']); foreach ($aResultsIml as $aItem) { - $aReturn[$aItem['cn'][0]] = array( + $aReturn[$aItem['cn'][0]] = [ 'dn' => $aItem['dn'], 'cn' => $aItem['cn'][0], '_description' => $aItem['description'][0], 'title' => $sTitle, 'description' => $sDescription, - ); + ]; } $oLdapIML->close(); ksort($aReturn); @@ -1467,10 +1526,12 @@ class project extends base } /** - * load config of a project - * @return boolean + * Load config of a project with given project id and turn a bool for success + * + * @param string $sId new project id of project to load + * @return boolean Success */ - public function setProjectById($sId) + public function setProjectById(string $sId): bool { if ($sId !== preg_replace('/[^a-z0-9\-\_]/i', '', $sId)) { $this->_errors[] = sprintf(t("class-project-error-config-wrongid"), htmlentities($sId)); @@ -1497,9 +1558,7 @@ class project extends base $sQuery = '(&(objectclass=hieraSource)(documentIdentifier=' . $sId . '))'; $aResult = $this->_ldapProjectSearch($sQuery); // echo '<pre>$aResult = ' . print_r($aResult, 1) . '</pre>'; - if ( - is_array($aResult) && $aResult[0] && array_key_exists('hieradata', $aResult[0]) - ) { + if (isset($aResult[0]['hieradata'])) { foreach ($aResult[0]['hieradata'] as $sLine) { // echo $sLine.'<br>'; if (preg_match('/^cfg=/', $sLine)) { @@ -1522,16 +1581,16 @@ class project extends base $sPluginName = (isset($this->_aPrjConfig['deploy']['enabled_rollout_plugin']) && $this->_aPrjConfig['deploy']['enabled_rollout_plugin']) ? $this->_aPrjConfig['deploy']['enabled_rollout_plugin'] : 'default'; - $this->oRolloutPlugin = false; + unset($this->oRolloutPlugin); try { require_once $this->_getPluginFilename('rollout', $sPluginName); $sPluginClassname = 'rollout_' . $sPluginName; - $this->oRolloutPlugin = new $sPluginClassname(array( + $this->oRolloutPlugin = new $sPluginClassname([ 'lang' => $this->_aConfig['lang'], 'phase' => false, 'globalcfg' => isset($this->_aConfig['plugins']['rollout'][$sPluginName]) ? $this->_aConfig['plugins']['rollout'][$sPluginName] : [], 'projectcfg' => $this->_aPrjConfig, - )); + ]); // print_r($this->_oRolloutPlugin->getPluginfos()); // print_r($this->_oRolloutPlugin->getName()); } catch (Exception $ex) { @@ -1542,12 +1601,12 @@ class project extends base /** * set a branchname * @param string $sBranchname name of the branch, i.e. "origin/master" - * @return bool + * @return string */ - public function setBranchname($sBranchname) + public function setBranchname(string $sBranchname): string { $this->_sBranchname = $sBranchname; - if ($this->_oVcs) { + if (isset($this->_oVcs) && $this->_oVcs) { if (method_exists($this->_oVcs, "setCurrentBranch")) { $this->_oVcs->setCurrentBranch($sBranchname); } @@ -1560,12 +1619,14 @@ class project extends base // ---------------------------------------------------------------------- /** - * send data to a tempfile for ajax polling - * @param type $sTmpFile - * @param type $sData - * @return boolean + * Store data to a tempfile (read by for ajax polling) and update actions box + * + * @param string $sData full output of all so far executed shell commands + * @param array $aActions for right output box: Array of actions with marker of current action + * see build() method for the structure + * @return bool|int */ - protected function _TempFill($sData, $aActions = array()) + protected function _TempFill(string $sData, array $aActions = []): bool|int { if (!$this->_sProcessTempOut) { return false; @@ -1592,12 +1653,13 @@ class project extends base } /** - * delete tempfile for ajax polling; if a directory is given as parameter - * the tmp file will be moved there + * Delete tempfile for ajax polling; if a directory is given as parameter + * the tmp file will be moved there. + * * @param string $sTempDir optional; target dir to copy; default=false (=delete file) * @return boolean */ - protected function _TempDelete($sTempDir = false) + protected function _TempDelete(string $sTempDir = ''): bool { if (!$this->_sProcessTempOut) { return false; @@ -1613,15 +1675,16 @@ class project extends base } /** - * get the name of the current branch (or default branch) - * @param bool $bReturnMasterIfEmpty flag: if there is no current branch then detect a master branch - * @return string + * Get the name of the current branch (or default branch). + * It returns false if the vcs was not initialized yet. + * + * @return string|bool */ - public function getBranchname($bReturnMasterIfEmpty = false) + public function getBranchname(): string|bool { $this->log(__FUNCTION__ . " start"); $this->_initVcs(); - if ($this->_oVcs) { + if (isset($this->_oVcs) && $this->_oVcs) { if (method_exists($this->_oVcs, "getCurrentBranch")) { $sCurrentBranch = $this->_oVcs->getCurrentBranch(true); // true means search for master branch if empty if ($sCurrentBranch) { @@ -1637,10 +1700,11 @@ class project extends base * Build a new package for the deployment. It will be put to the queue * of the first active phase (i.e. preview). * If there is no deployment time range it will be deployed too. - * @global type $aParams - * @return boolean|string + * + * @global string $sTmpFile + * @return boolean|string false or HTML code */ - public function build($sTmpFile = false) + public function build(string $sTmpFile = ''): bool|string { $this->log(__FUNCTION__ . " start"); if (!$this->oUser->hasPermission("project-action-build")) { @@ -1649,22 +1713,22 @@ class project extends base global $aParams; $sReturn = false; - $aActionList = array( + $aActionList = [ 'iActive' => 0, 'label' => t('build'), - 'actions' => array( - array('label' => t('class-project-build-label-cleanup-builds')), - array('label' => t('class-project-build-label-create-workdir')), - array('label' => t('class-project-build-label-get-sources-from-version-control')), - array('label' => t('class-project-build-label-execute-hook-postclone')), - array('label' => t('class-project-build-label-copy-default-structure')), - array('label' => t('class-project-build-label-execute-hook-precompress')), - array('label' => t('class-project-build-label-cleanup-project')), - array('label' => t('class-project-build-label-create-package')), - array('label' => t('class-project-build-label-remove-workdir')), - array('label' => t('class-project-build-label-queue-to-first-active-phase')), - ), - ); + 'actions' => [ + ['label' => t('class-project-build-label-cleanup-builds')], + ['label' => t('class-project-build-label-create-workdir')], + ['label' => t('class-project-build-label-get-sources-from-version-control')], + ['label' => t('class-project-build-label-execute-hook-postclone')], + ['label' => t('class-project-build-label-copy-default-structure')], + ['label' => t('class-project-build-label-execute-hook-precompress')], + ['label' => t('class-project-build-label-cleanup-project')], + ['label' => t('class-project-build-label-create-package')], + ['label' => t('class-project-build-label-remove-workdir')], + ['label' => t('class-project-build-label-queue-to-first-active-phase')], + ], + ]; $this->_setProcessOutFile($sTmpFile); $this->_iRcAll = 0; @@ -1684,7 +1748,7 @@ class project extends base $this->_TempFill($sReturn, $aActionList); $this->_initVcs(); - if (!$this->_oVcs) { + if (!isset($this->_oVcs) || !$this->_oVcs) { $sError = sprintf(t('class-project-error-build-type-not-supported'), $this->_aPrjConfig["build"]["type"]); $this->_logaction($sError, __FUNCTION__, "error"); return $this->_oHtml->getBox("error", $sError . $sReturn); @@ -1723,10 +1787,10 @@ class project extends base $sReturn .= '<pre>' . $this->_oVcs->getSources($sTempBuildDir) . '</pre>'; - $aRepodata=$this->getRepoRevision(); - $sRevisionShort=substr($aRepodata['revision'], 0, 8); + $aRepodata = $this->getRepoRevision(); + $sRevisionShort = substr($aRepodata['revision'], 0, 8); - $sReturn .= $this->_oHtml->getBox("info", t('commitmessage') . '<pre>'.htmlentities($aRepodata['message']).'</pre>'); + $sReturn .= $this->_oHtml->getBox("info", t('commitmessage') . '<pre>' . htmlentities($aRepodata['message']) . '</pre>'); $sReturn .= $this->_execAndSend("ls -lisa $sTempBuildDir"); if (!$this->_iRcAll == 0) { @@ -1757,24 +1821,24 @@ class project extends base # /task#5047 } - $aCivars=[ - 'branch'=>$aRepodata['branch'], - 'branch_short'=>$aRepodata['shortname'], - 'branch_type'=>$aRepodata['type'], - 'revision'=>$aRepodata['revision'], - 'revision_short'=>$sRevisionShort, - 'imagepart'=>$aRepodata['type']=='tags' - ? $aRepodata['shortname'] - : $sRevisionShort - , + $aCivars = [ + 'branch' => $aRepodata['branch'], + 'branch_short' => $aRepodata['shortname'], + 'branch_type' => $aRepodata['type'], + 'revision' => $aRepodata['revision'], + 'revision_short' => $sRevisionShort, + 'imagepart' => $aRepodata['type'] == 'tags' + ? $aRepodata['shortname'] + : $sRevisionShort + , // 'message'=>$aRepodata['message'], ]; $sCfgContent .= "\n" . "# ---------- generated CI SERVER variables\n" . "\n" - ; - foreach ($aCivars as $sKey => $value){ + ; + foreach ($aCivars as $sKey => $value) { $sCfgContent .= "export CI_$sKey=\"$value\"; \n"; } @@ -1860,7 +1924,7 @@ class project extends base // cleanup .git, .svn, ... // -------------------------------------------------- $sReturn .= '<h3>' . t('class-project-build-label-cleanup-project') . '</h3>'; - if ($this->_oVcs) { + if (isset($this->_oVcs) && $this->_oVcs) { $this->_oVcs->cleanupWorkdir($sTempBuildDir); } @@ -1875,13 +1939,10 @@ class project extends base // -------------------------------------------------- $sReturn .= '<h3>' . t('class-project-build-label-create-package') . '</h3>'; // public_html must exist - if ( - array_key_exists('haspublic', $this->_aPrjConfig["build"]) - && $this->_aPrjConfig["build"]["haspublic"][0] - ) { + if (isset($this->_aPrjConfig["build"]['haspublic'][0])) { $sWebroot = false; - $sWebroot1 = $sTempBuildDir . '/public_html'; - $sWebroot2 = $sTempBuildDir . '/public'; + $sWebroot1 = "$sTempBuildDir/public_html"; + $sWebroot2 = "$sTempBuildDir/public"; if (file_exists($sWebroot1)) { $sWebroot = $sWebroot1; } @@ -1914,20 +1975,20 @@ class project extends base $sInfoFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getInfofile($sFirstLevel, "deployed")); $sPackageFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getPackagefile($sFirstLevel, "deployed")); - $aInfos = array( + $aInfos = [ 'date' => $sTs, 'version' => $sTs2, // 'branch' => $sBranch, - 'branch'=>$aRepodata['branch'], - 'branch_short'=>$aRepodata['shortname'], - 'branch_type'=>$aRepodata['type'], - 'revision'=>$aRepodata['revision'], - 'revision_short'=>$sRevisionShort, + 'branch' => $aRepodata['branch'], + 'branch_short' => $aRepodata['shortname'], + 'branch_type' => $aRepodata['type'], + 'revision' => $aRepodata['revision'], + 'revision_short' => $sRevisionShort, - 'imagepart'=>$aCivars['imagepart'], + 'imagepart' => $aCivars['imagepart'], 'message' => $aRepodata['message'], - ); + ]; /* "user": "' . $aParams["inputUser"] . '", "remark": "' . $aParams["inputComment"] . '" @@ -1966,16 +2027,16 @@ class project extends base try { include_once $this->_getPluginFilename('build', $sPluginName); $sPluginClassname = 'build_' . $sPluginName; - $oPlugin = new $sPluginClassname(array( + $oPlugin = new $sPluginClassname([ 'lang' => $this->_aConfig['lang'], 'workdir' => $sTempBuildDir, 'outfile' => $sPackageFileArchiv, - )); + ]); } catch (Exception $ex) { return $this->_oHtml->getBox( "error", "FAILED to initialize build plugin " . $sPluginName . '<br>' - . $sReturn + . $sReturn ); } @@ -2040,23 +2101,24 @@ class project extends base } /** - * put a packaged version into the queue of a specified phase - * @param string $sPhase name of the phase + * Put a packaged version into the queue of a specified phase + * + * @param string $sPhase name of the phase that gets the new version * @param string $sVersion version - * @return string + * @return string The HTML code */ - public function queue($sPhase, $sVersion) + public function queue(string $sPhase, string $sVersion): string { - $aActionList = array( + $aActionList = [ 'iActive' => 0, 'label' => t("queue"), - 'actions' => array( - array('label' => t("class-project-queue-label-checks")), - array('label' => t("class-project-queue-label-remove-existing-version")), - array('label' => t("class-project-queue-label-link-new-version")), - array('label' => t("class-project-queue-label-deploy")), - ), - ); + 'actions' => [ + ['label' => t("class-project-queue-label-checks")], + ['label' => t("class-project-queue-label-remove-existing-version")], + ['label' => t("class-project-queue-label-link-new-version")], + ['label' => t("class-project-queue-label-deploy")], + ], + ]; $this->_logaction(t('starting') . " queue($sPhase, $sVersion)", __FUNCTION__); $sReturn = "<h2> " . t("queue") . " " . $this->getLabel() . " :: $sPhase</h2>"; $this->_TempFill($sReturn, $aActionList); @@ -2124,14 +2186,15 @@ class project extends base } /** - * deploy a queued package - this moves the queue into the repo directory - * and will be installed on server within 30 min. + * Deploy a queued package - this moves the queue into the repo directory. * This method checks the deploy times - * @param string $sPhase which queue of which phase we want to install in server + * It returns the output to show in browser + * + * @param string $sPhase the queue of which phase we want to install in server * @param bool $bIgnoreDeploytimes flag; if true it will override time windows - * @return boolean|string + * @return string The HTML output */ - public function deploy($sPhase, $bIgnoreDeploytimes = false) + public function deploy(string $sPhase, bool $bIgnoreDeploytimes = false): string { $this->log(__FUNCTION__ . " start"); if ( @@ -2139,16 +2202,16 @@ class project extends base ) { return $this->oUser->showDenied(); } - $aActionList = array( + $aActionList = [ 'iActive' => 0, 'label' => t("deploy"), - 'actions' => array( - array('label' => t("class-project-deploy-label-checks")), - array('label' => t("class-project-deploy-label-activate-queued-version")), - array('label' => t("class-project-deploy-label-synch-packages")), - array('label' => t("class-project-deploy-label-install-on-target")), - ), - ); + 'actions' => [ + ['label' => t("class-project-deploy-label-checks")], + ['label' => t("class-project-deploy-label-activate-queued-version")], + ['label' => t("class-project-deploy-label-synch-packages")], + ['label' => t("class-project-deploy-label-install-on-target")], + ], + ]; $sReturn = "<h2>" . t("deploy") . " " . $this->getLabel() . " :: $sPhase</h2>"; $this->_TempFill($sReturn, $aActionList); @@ -2244,10 +2307,10 @@ class project extends base // synch packages // -------------------------------------------------- // $sReturn.=$this->_execAndSend("ln -s $sLinkTarget $sLinkName"); - if (array_key_exists('mirrorPackages', $this->_aConfig) && count($this->_aConfig['mirrorPackages'])) { + if (isset($this->_aConfig['mirrorPackages']) && count($this->_aConfig['mirrorPackages'])) { foreach ($this->_aConfig['mirrorPackages'] as $sLabel => $aTarget) { $sReturn .= '<h3>' . sprintf(t("class-project-info-deploy-synching-package"), $sLabel) . "</h3>"; - if (array_key_exists('type', $aTarget)) { + if (isset($aTarget['type'])) { $sCmd = false; // $sSource=$this->_aConfig["packageDir"]."/$sPhase/*"; $sSource = $sRepoLink; @@ -2280,7 +2343,7 @@ class project extends base // -------------------------------------------------- // run action to install // -------------------------------------------------- - $sDeploymethod = array_key_exists("deploymethod", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["deploymethod"] : "none"; + $sDeploymethod = isset($this->_aPrjConfig["phases"][$sPhase]["deploymethod"]) ? $this->_aPrjConfig["phases"][$sPhase]["deploymethod"] : "none"; // $sTargethosts = array_key_exists("hosts", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["hosts"] : ''; $sReturn .= '<h3>' . t("class-project-info-deploy-start-by-method") . ' :: ' . $sDeploymethod . '</h3>' @@ -2347,12 +2410,13 @@ class project extends base } /** - * accept a the installed version in a phase and put this version + * Accept a the installed version of the given phase and put this version * to the queue of the next phase. - * @param string $sPhase which queue of which phase we want to install in server - * @return type + * + * @param string $sPhase phase to accept to be rolled out on the next phase + * @return string The HTML code */ - public function accept($sPhase) + public function accept(string $sPhase): string { $this->log(__FUNCTION__ . " start"); if ( @@ -2388,22 +2452,26 @@ class project extends base } /** - * save POSTed data as project config - * @return boolean + * Save data as project config. + * If no data were given then $_POST is used. + * It returns bool with success state or a string with deny error message + * + * @param array $aData optional: data to write + * @return boolean|string */ - public function saveConfig($aData = false) + public function saveConfig(array $aData = []): bool|string { $this->log(__FUNCTION__ . " start"); if (!$this->oUser->hasPermission("project-action-setup")) { return $this->oUser->showDenied(); } $this->_logaction(t('starting') . " saveConfig(...)", __FUNCTION__); - if (!$aData) { + if (!count($aData)) { $aData = $_POST; } - foreach (array('id', 'label', 'description', 'contact', 'build', 'fileprefix', 'phases') as $sKey) { - if (!array_key_exists($sKey, $aData)) { + foreach (['id', 'label', 'description', 'contact', 'build', 'fileprefix', 'phases'] as $sKey) { + if (!isset($aData[$sKey])) { $this->_logaction(t('abortet') . " missing key $sKey in savedata", __FUNCTION__, "error"); return false; } @@ -2411,8 +2479,8 @@ class project extends base $sId = $aData["id"]; // remove unwanted items - foreach (array("setupaction", "prj", "id") as $s) { - if (array_key_exists($s, $aData)) { + foreach (["setupaction", "prj", "id"] as $s) { + if (isset($aData[$s])) { unset($aData[$s]); } } @@ -2435,17 +2503,17 @@ class project extends base $sDn = 'documentIdentifier=' . $sId . ',' . $this->_aConfig['projects']['ldap']['DnProjects']; - $aItem = array( - 'objectClass' => array( + $aItem = [ + 'objectClass' => [ 'document', 'hieraSource', 'top', - ), - 'hieraData' => array( + ], + 'hieraData' => [ 'cfg=' . json_encode($aData), 'updated=' . date("Y-m-d H:i:s") . ' by ' . $this->oUser->getUsername(), - ) - ); + ] + ]; require_once("ldap.class.php"); $oLdapIML = new imlldap($this->_aConfig['projects']['ldap']); @@ -2482,12 +2550,13 @@ class project extends base } /** - * create a new project; it returns the error message if it fails and + * Create a new project; it returns the error message if it fails and * an empty string if it was successful. + * * @param string $sId id * @return string */ - public function create($sId) + public function create(string $sId): string { $this->log(__FUNCTION__ . " start"); if (!$this->oUser->hasPermission("project-action-create")) { @@ -2520,24 +2589,24 @@ class project extends base $this->_readConfig(); $this->_aConfig["id"] = $sId; - $this->_aPrjConfig = array( + $this->_aPrjConfig = [ "id" => $sId, // for saveConfig "label" => "$sId", "fileprefix" => "$sId", "description" => '', "contact" => '', - "build" => array( + "build" => [ "type" => "", "ssh" => "", "auth" => "", "webaccess" => "", - ), - "phases" => array( - "preview" => array(), - "stage" => array(), - "live" => array(), - ), - ); + ], + "phases" => [ + "preview" => [], + "stage" => [], + "live" => [], + ], + ]; $this->_verifyConfig(); // check skeleton $bReturn = $this->saveConfig($this->_aPrjConfig); if (!$bReturn) { @@ -2552,11 +2621,15 @@ class project extends base } /** - * delete a project; it returns a string with errormessage; false = no error - * @param array $aOptions - * @return boolean|string + * Delete a project; it returns a string with errormessage; empty string = no error + * + * @param array $aOptions array with enabled actions + * - bRemoveRepolinks + * - bRemoveArchive + * - bRemoveConfig + * @return string */ - public function delete($aOptions = array()) + public function delete(array $aOptions = []): string { $this->log(__FUNCTION__ . " start"); if (!$this->oUser->hasPermission("project-action-delete")) { @@ -2568,9 +2641,9 @@ class project extends base } $this->_logaction(t('starting') . " delete()", __FUNCTION__); - // (array("bRemoveRepolinks", "bRemoveArchive", "bRemoveConfig") + // ["bRemoveRepolinks", "bRemoveArchive", "bRemoveConfig"] // --- remove links in phases directory to built archives - if (array_key_exists("bRemoveRepolinks", $aOptions) && $aOptions["bRemoveRepolinks"]) { + if (isset($aOptions["bRemoveRepolinks"]) && $aOptions["bRemoveRepolinks"]) { echo "DELETE Repo-Links ...<br>"; foreach (array_keys($this->getPhases()) as $sPhase) { @@ -2587,11 +2660,11 @@ class project extends base } } } - if (array_key_exists("bRemoveArchive", $aOptions) && $aOptions["bRemoveArchive"]) { + if (isset($aOptions["bRemoveArchive"]) && $aOptions["bRemoveArchive"]) { echo "DELETE built Archives ...<br>"; $this->cleanupArchive(true); // true to delete all } - if (array_key_exists("bRemoveConfig", $aOptions) && $aOptions["bRemoveConfig"]) { + if (isset($aOptions["bRemoveConfig"]) && $aOptions["bRemoveConfig"]) { echo "DELETE Config ...<br>"; // echo "config file: $sCfgfile<br>"; if (file_exists($sCfgfile . ".ok")) { @@ -2609,6 +2682,6 @@ class project extends base } $this->_sendMessage(t('finished') . " delete()"); $this->_logaction(t('finished') . " delete()", __FUNCTION__, "success"); - return false; + return ''; } } diff --git a/public_html/deployment/classes/project_gui.class.php b/public_html/deployment/classes/project_gui.class.php index de11b83d2598dfd5d299af5d0caccc22412479ba..d6fef25337b20a041746c2bc9d7a85386e50b422 100644 --- a/public_html/deployment/classes/project_gui.class.php +++ b/public_html/deployment/classes/project_gui.class.php @@ -13,76 +13,79 @@ require_once 'htmlguielements.class.php'; --------------------------------------------------------------------- 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + (...) + 2024-08-26 Axel php8 only; added variable types; short array syntax ###################################################################### */ /** * class for single project */ // class project { -class projectgui extends project { - - /** - * constructor - * @param string $sId id of the project - public function __construct($sId = false) { - parent::__construct($sId); - $this->_oHtml = new htmlguielements(); - } - */ +class projectgui extends project +{ // ---------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------- /** - * return html code for a div with background color based on a checksum of the given text - * @param string $sText text that is used for checksum; if false ist returns a gray + * Return html code for a div with background color based on a checksum of the given text + * + * @param null|string $sText text that is used for checksum; if false ist returns a gray * @param string $sContent optional: text to show - * @return string + * @return string The HTML code */ - private function _getChecksumDiv($sText, $sContent='', $sBarHeight='3px') { - if ($sText){ - + private function _getChecksumDiv(null|string $sText = '', string $sContent = '', string $sBarHeight = '3px'): string + { + if ($sText) { + // color ranges in decimal values for RGB from ... to - $iFgStart=60; $iFgEnd=160; - $iBgStart=200; $iBgEnd=250; + $iFgStart = 60; + $iFgEnd = 160; + $iBgStart = 200; + $iBgEnd = 250; - $iFgStart=60; $iFgEnd=160; - $iBgStart=190; $iBgEnd=250; + $iFgStart = 60; + $iFgEnd = 160; + $iBgStart = 190; + $iBgEnd = 250; // deivider: 3 digits of md5 will be extracted - $iFgDivider=16*16*16/($iFgEnd-$iFgStart); - $iBgDivider=16*16*16/($iBgEnd-$iBgStart); - - $sHash=md5($sText); - $sColor='' - . 'color: rgba(' - . ($iFgStart + round(hexdec(substr($sHash,0,3))/$iFgDivider)) . ',' - . ($iFgStart + round(hexdec(substr($sHash,3,3))/$iFgDivider)) . ',' - . ($iFgStart + round(hexdec(substr($sHash,6,3))/$iFgDivider)) . ',' + $iFgDivider = 16 * 16 * 16 / ($iFgEnd - $iFgStart); + $iBgDivider = 16 * 16 * 16 / ($iBgEnd - $iBgStart); + + $sHash = md5($sText); + $sColor = '' + . 'color: rgba(' + . ($iFgStart + round(hexdec(substr($sHash, 0, 3)) / $iFgDivider)) . ',' + . ($iFgStart + round(hexdec(substr($sHash, 3, 3)) / $iFgDivider)) . ',' + . ($iFgStart + round(hexdec(substr($sHash, 6, 3)) / $iFgDivider)) . ',' . '1' . ');' - . 'background: rgba(' - . ($iBgStart + round(hexdec(substr($sHash,0,3))/$iBgDivider)) . ',' - . ($iBgStart + round(hexdec(substr($sHash,3,3))/$iBgDivider)) . ',' - . ($iBgStart + round(hexdec(substr($sHash,6,3))/$iBgDivider)) . ',' + . 'background: rgba(' + . ($iBgStart + round(hexdec(substr($sHash, 0, 3)) / $iBgDivider)) . ',' + . ($iBgStart + round(hexdec(substr($sHash, 3, 3)) / $iBgDivider)) . ',' + . ($iBgStart + round(hexdec(substr($sHash, 6, 3)) / $iBgDivider)) . ',' . '1' . ');' - ; + ; } else { $sColor = "color: #888; background: #ccc;"; } - return '<div style="' . $sColor . ' border-top: '.$sBarHeight.' solid;">'.($sContent ? $sContent : ' ').'</div>'; + return '<div style="' . $sColor . ' border-top: ' . $sBarHeight . ' solid;">' . ($sContent ?: ' ') . '</div>'; } /** - * get html code for the colored bar on top of each phase detail items + * Get html code for the colored bar on top of each phase detail items. + * It returns false of revision number was not found in the given phase + place + * * @param string $sPhase phase of a project * @param string $sPlace place in the given phase - * @return string + * @return bool|string The HTML code */ - private function _renderBar($sPhase, $sPlace, $sBarHeight='3px') { + private function _renderBar(string $sPhase, string $sPlace, string $sBarHeight = '3px'): bool|string + { $aDataPhase = $this->getPhaseInfos($sPhase); $aData = $aDataPhase[$sPlace]; if (!array_key_exists("revision", $aData)) { @@ -91,39 +94,47 @@ class projectgui extends project { return $this->_getChecksumDiv($aData["revision"], '', $sBarHeight); } - private function _renderHostsData($aData) { + /** + * Render deploy infos: show hosts and its installed revisions + * @param array $aData deployment metadata + * @return string + */ + private function _renderHostsData(array $aData): string + { $sReturn = ''; - if (array_key_exists('_hosts', $aData)) { - + if (isset($aData['_hosts'])) { + // $sReturn.= print_r($aData['_hosts'], 1); - $sReturn.= '<div class="hosts">' - . '<br><strong>' . t('hosts') . ':</strong><br>' + $sReturn .= '<div class="hosts">' + . '<br><strong>' . t('hosts') . ':</strong><br>' ; foreach ($aData['_hosts'] as $sHostname => $aHostinfos) { $oUpdateDate = date("U", strtotime($aHostinfos['time'])); $iAgeUpdate = round((date("U") - $oUpdateDate) / 60); - $sAge = $iAgeUpdate < 60 * 60 * 13 ? $iAgeUpdate . " min" : "??"; - - $sReturn.= '<div class="host">' - . $this->_getChecksumDiv( - $aHostinfos['_data']['revision'], - $this->_oHtml->getIcon('host').'<br>' . $sHostname - ) - . "($sAge)" - . '</div>' + $sAge = $iAgeUpdate < 60 * 60 * 13 ? "$iAgeUpdate min" : "??"; + + $sReturn .= '<div class="host">' + . $this->_getChecksumDiv( + $aHostinfos['_data']['revision'], + $this->_oHtml->getIcon('host') . '<br>' . $sHostname + ) + . "($sAge)" + . '</div>' ; } - $sReturn.= '</div><div style="clear: both;"></div>'; + $sReturn .= '</div><div style="clear: both;"></div>'; } return $sReturn; } /** - * get html code for list of hosts in a phase + * Get html code for list of hosts in a phase + * * @param string $sPhase phase of a project * @return string */ - private function _renderHosts($sPhase) { + private function _renderHosts(string $sPhase): string + { $aDataPhase = $this->getPhaseInfos($sPhase); if (is_array($aDataPhase) && array_key_exists('deployed', $aDataPhase)) { return $this->_renderHostsData($aDataPhase['deployed']); @@ -132,25 +143,27 @@ class projectgui extends project { } /** - * get html code for list of files in a phase + * Get html code for list of files in a phase + * * @param string $sPhase phase of a project * @return string */ - private function _renderFiles($sPhase) { + private function _renderFiles(string $sPhase): string + { $sReturn = ''; $aFiles = $this->getBuildfilesByPlace($sPhase, 'ready2install'); if (!$aFiles || !$aFiles['filecount']) { return ''; } - $sReturn.='<strong>' . t("filelist") . '</strong> (' . $aFiles['filecount'] . '):<br>'; + $sReturn .= '<strong>' . t("filelist") . '</strong> (' . $aFiles['filecount'] . '):<br>'; foreach ($aFiles['files'] as $sFilename => $aData) { - $sReturn.='<div class="file file-' . $aData['type'] . ' fileext-' . $aData['extension'] . '" title="' . $sFilename . ' (' . $aData['type'] . ')">' - . $aData['icon'] . $sFilename - // . ' ('.$aData['type'].')' - . '</div>' + $sReturn .= '<div class="file file-' . $aData['type'] . ' fileext-' . $aData['extension'] . '" title="' . $sFilename . ' (' . $aData['type'] . ')">' + . $aData['icon'] . $sFilename + // . ' ('.$aData['type'].')' + . '</div>' ; } - $sReturn.='(' . $aFiles['totalsize-hr'] . ')'; + $sReturn .= '(' . $aFiles['totalsize-hr'] . ')'; return $sReturn; } @@ -160,56 +173,57 @@ class projectgui extends project { /** - * render html for a row with td for all places of a phase + * Get html for a row with td for all places of a phase + * * @param string $sPhase phase * @param bool $bActions draw action links (deploy, accept) on/ off * @param bool $bLong use long variant to display infos? - * @return string|boolean + * @return string */ - public function renderAllPhaseDetails($sPhase, $bActions = true, $bLong = true) { - if (!$sPhase) { - return false; - } + public function renderAllPhaseDetails(string $sPhase, bool $bActions = true, bool $bLong = true): string + { if (!$this->isActivePhase($sPhase)) { return ' <td class="td-phase-' . $sPhase . ' td-phase-inactive ' . $this->_aConfig["id"] . '" colspan="' . count($this->_aPlaces) . '"> - <div class="versioninfo center inactive">' . $this->_oHtml->getIcon('sign-info').t('inactive') . '</div> + <div class="versioninfo center inactive">' . $this->_oHtml->getIcon('sign-info') . t('inactive') . '</div> </td>'; } $sRow2 = false; - $aRows = array(); + $aRows = []; $sLastPlace = ''; - + foreach (array_keys($this->_aPlaces) as $sPlace) { $aRows[$sPlace] = $this->renderPhaseDetail($sPhase, $sPlace, $bActions, $bLong); - + // generate ">>" sign for lastly generated td - if ($sLastPlace && array_key_exists("version", $this->_aData["phases"][$sPhase][$sLastPlace]) - && array_key_exists("version", $this->_aData["phases"][$sPhase][$sPlace]) - && $this->_aData["phases"][$sPhase][$sLastPlace]["version"] == $this->_aData["phases"][$sPhase][$sPlace]["version"] - && !$bLong + if ( + $sLastPlace && array_key_exists("version", $this->_aData["phases"][$sPhase][$sLastPlace]) + && array_key_exists("version", $this->_aData["phases"][$sPhase][$sPlace]) + && $this->_aData["phases"][$sPhase][$sLastPlace]["version"] == $this->_aData["phases"][$sPhase][$sPlace]["version"] + && !$bLong ) { $aRows[$sLastPlace] = $this->_renderBar($sPhase, $sPlace) . "»"; } $sLastPlace = $sPlace; } - + foreach (array_keys($this->_aPlaces) as $sPlace) { - $sRow2.='<td class=" td-phase-'.$sPhase.' td-place-'.$sPlace.' td' . $this->_aConfig["id"] . '">' . $aRows[$sPlace] . '</td>'; + $sRow2 .= '<td class=" td-phase-' . $sPhase . ' td-place-' . $sPlace . ' td' . $this->_aConfig["id"] . '">' . $aRows[$sPlace] . '</td>'; } return $sRow2; } /** - * return html code for current project errors by rendering a box per error in $this->_errors + * Get html code for current project errors by rendering a box per error in $this->_errors * @return string */ - public function renderErrorBoxes(){ - $sReturn=''; - if(count($this->_errors)){ - foreach($this->_errors as $sError){ - $sReturn.=$this->_oHtml->getBox("error", $sError); + public function renderErrorBoxes(): string + { + $sReturn = ''; + if (count($this->_errors)) { + foreach ($this->_errors as $sError) { + $sReturn .= $this->_oHtml->getBox("error", $sError); } } return $sReturn; @@ -220,22 +234,25 @@ class projectgui extends project { * fix output of commit message as html * This is a compatibility function for older builds * - * @param string $sMessage git commit message + * @param null|string $sMessage git commit message * @return string */ - public function transformCommitMessage($sMessage){ - if(strstr($sMessage, '<br>Date:')){ - $_aReplace=[ + public function transformCommitMessage(null|string $sMessage): string + { + if (strstr($sMessage, '<br>Date:')) { + $_aReplace = [ '<br>Author:' => "\nAuthor:", '<br>Date:' => "\nDate:", '<br><br>' => "\n\n", ]; - $sMessage=str_replace(array_keys($_aReplace), array_values($_aReplace), $sMessage)." *"; + $sMessage = str_replace(array_keys($_aReplace), array_values($_aReplace), $sMessage) . " *"; } return htmlentities($sMessage); } + /** - * render html code for info link that shows popup with metadata on mouseover + * Get html code for info link that shows popup with metadata on mouseover + * * @param array $aInfos metainfos of the package (from json file) * one of ok=1|error=message - status key * date - timestamp of build @@ -249,63 +266,65 @@ class projectgui extends project { * hpos - horizontal position; one of left|right; default: right * @return string */ - public function renderInfoLink($aInfos, $aOptions = array()) { + public function renderInfoLink(array $aInfos, array $aOptions = []): string + { $sReturn = ''; $bIsError = false; $this->_oHtml = new htmlguielements(); - $sInfos=''; - $sTitle=''; + $sInfos = ''; + $sTitle = ''; if (array_key_exists("title", $aOptions) && $aOptions["title"]) { - $sTitle.=$aOptions["title"]; + $sTitle .= $aOptions["title"]; } if (array_key_exists("ok", $aInfos)) { $sLinktitle = t('infos'); if (array_key_exists("message", $aInfos)) { - $sInfos.=$this->_getChecksumDiv($aInfos["revision"], - $this->_oHtml->getIconByType('calendar') . t('build-from') . ' ' . date("d.m.Y H:i:s", strtotime($aInfos["date"])) . '<br>' - . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aInfos["branch"] . '<br>' - . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aInfos["revision"]) . '<br>' - . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ': ' - ) - . '<pre>' . $this->transformCommitMessage($aInfos["message"]) . '</pre>'; + $sInfos .= $this->_getChecksumDiv( + $aInfos["revision"], + $this->_oHtml->getIconByType('calendar') . t('build-from') . ' ' . date("d.m.Y H:i:s", strtotime($aInfos["date"])) . '<br>' + . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aInfos["branch"] . '<br>' + . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aInfos["revision"]) . '<br>' + . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ': ' + ) + . '<pre>' . $this->transformCommitMessage($aInfos["message"]) . '</pre>'; if (array_key_exists("more", $aOptions)) { - $sInfos.=$aOptions["more"]; + $sInfos .= $aOptions["more"]; } } } else { $bIsError = true; if (!$sTitle) { - $sTitle.=' ' . t('error'); + $sTitle .= ' ' . t('error'); } $sLinktitle = t('error'); - $sInfos = $this->_oHtml->getBox('error', '') . '<p>'.$aInfos["error"].'</p>'; + $sInfos = $this->_oHtml->getBox('error', '') . '<p>' . $aInfos["error"] . '</p>'; } - $sInfos.=$this->_renderHostsData($aInfos, ''); + $sInfos .= $this->_renderHostsData($aInfos); if (array_key_exists("label", $aOptions) && $aOptions["label"]) { - $sLinktitle.=$aOptions["label"]; + $sLinktitle .= $aOptions["label"]; } // render html $sId = 'info' . md5($sInfos); $sReturn = '<a href="#" class="btn ' . ($bIsError ? 'btn-danger' : 'btn-default') . '" title="" - onclick="showIdAsModalMessage(\'' . $sId . '\', \''.$sTitle.'\'); return false;" + onclick="showIdAsModalMessage(\'' . $sId . '\', \'' . $sTitle . '\'); return false;" >' - . $this->_oHtml->getIcon($bIsError ? 'sign-error' : 'sign-info') // ... '<i class="fa fa-info"></i> ' - . $sLinktitle - . '</a><div id="' . $sId . '" style="display: none;" ' - ; + . $this->_oHtml->getIcon($bIsError ? 'sign-error' : 'sign-info') // ... '<i class="fa fa-info"></i> ' + . $sLinktitle + . '</a><div id="' . $sId . '" style="display: none;" ' + ; if (array_key_exists("hpos", $aOptions)) { - $sReturn.=' class="' . $aOptions["hpos"] . '"'; + $sReturn .= ' class="' . $aOptions["hpos"] . '"'; } - $sReturn.='>'; + $sReturn .= '>'; if ($sTitle) { // $sReturn.='<span class="title">' . $sTitle . '</span><br><br>'; } - $sReturn.=$sInfos . '</div>'; + $sReturn .= $sInfos . '</div>'; if ($bIsError) { // $sReturn = '<div class="error">' . $sReturn . '</div>'; @@ -315,52 +334,61 @@ class projectgui extends project { } /** - * render html for a colored link to any project action + * Get html for a colored link to any project action + * * @param string $sFunction name of the action; one of accept|build|cleanup|deploy|new|overview|phase|rollback|setup * @param string $sPhase current phase where to place the link * @return string */ - public function renderLink($sFunction, $sPhase = false, $sVersion = false) { + public function renderLink(string $sFunction, string $sPhase = '', string $sVersion = ''): string + { $sFirst = $this->getNextPhase(); $sNext = $this->getNextPhase($sPhase); - $aLinkdata = array( - 'default' => array('class' => ''), - 'accept' => array('class' => $sNext, + $aLinkdata = [ + 'default' => ['class' => ''], + 'accept' => [ + 'class' => $sNext, 'hint' => sprintf(t("accept-hint"), $sPhase, $sNext), 'label' => t('accept'), - ), - 'build' => array('class' => $sFirst, + ], + 'build' => [ + 'class' => $sFirst, 'hint' => sprintf(t("build-hint"), $sFirst), 'label' => t('build'), 'role' => 'buildProject' - ), - 'cleanup' => array('class' => ''), - 'deploy' => array('class' => $sPhase, + ], + 'cleanup' => ['class' => ''], + 'deploy' => [ + 'class' => $sPhase, 'hint' => sprintf(t("deploy-hint"), $sPhase), 'label' => t('deploy'), - ), - 'new' => array( + ], + 'new' => [ 'hint' => t("new-project-hint"), 'label' => t('new-project'), - ), - 'overview' => array('class' => '', + ], + 'overview' => [ + 'class' => '', 'hint' => t('menu-project-home') . ' [' . $this->getLabel() . ']', 'label' => $this->getLabel() - ), - 'phase' => array('icon' => $this->_oHtml->getIcon('phase'), 'class' => $sPhase, + ], + 'phase' => [ + 'icon' => $this->_oHtml->getIcon('phase'), + 'class' => $sPhase, 'hint' => sprintf(t('phase-details-hint'), $sPhase), 'label' => sprintf(t('phase-details'), $sPhase), - ), - 'rollback' => array('class' => $sPhase, + ], + 'rollback' => [ + 'class' => $sPhase, 'hint' => sprintf(t('rollback-hint'), $sPhase, $sVersion), 'label' => t('rollback') - ), - 'setup' => array('class' => $sPhase, + ], + 'setup' => [ + 'class' => 'btn', 'hint' => sprintf(t('setup-hint'), $sPhase, $sVersion), 'label' => t('setup'), - 'class' => 'btn' - ), - ); + ], + ]; /* if (!$this->oUser->hasRole("project-action-$sFunction")){ // $sClass .= ' disabled'; @@ -372,12 +400,11 @@ class projectgui extends project { $sRole = ''; $sOnMouseover = ''; $sOnMouseout = ''; - switch($sFunction){ + switch ($sFunction) { case 'accept'; $sRole = 'developer'; if ($sNext == "live") { $sRole = 'pl'; - // $aLinkdata[$sFunction]['icon']='glyphicon glyphicon-star'; } $sOnMouseover = '$(\'.td-phase-' . $sNext . '.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');'; $sOnMouseout = '$(\'.td-phase-' . $sNext . '.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');'; @@ -390,11 +417,11 @@ class projectgui extends project { case 'deploy'; $sRole = 'developer'; $sOnMouseover = '$(\'.td-phase-' . $sPhase . '.td-place-ready2install.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');' - .'$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');' - ; + . '$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');' + ; $sOnMouseout = '$(\'.td-phase-' . $sPhase . '.td-place-ready2install.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');' - .'$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');' - ; + . '$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');' + ; break; } @@ -409,13 +436,13 @@ class projectgui extends project { $sLink = "/deployment/" . ($this->_aConfig["id"] ? $this->_aConfig["id"] : 'all/setup') . "/"; if ($sFunction != "overview") { - $sLink.="$sFunction/"; + $sLink .= "$sFunction/"; } if ($sPhase) { - $sLink.="$sPhase/"; + $sLink .= "$sPhase/"; } if ($sVersion) { - $sLink.="$sVersion/"; + $sLink .= "$sVersion/"; } if (!$this->oUser->hasPermission("project-action-$sFunction")) { // $sClass .= ' disabled'; @@ -423,46 +450,47 @@ class projectgui extends project { } // $sClass='btn ' . (strstr('btn-', $sClass) ? '': 'btn-default ') .$sClass; - return $this->_oHtml->getLinkButton(array( - 'href' => $sLink, - 'title' => $sHint, - 'class' => $sClass, - 'type' => $sFunction, - 'onmouseover' => $sOnMouseover, - 'onmouseout' => $sOnMouseout, - 'label' => $sLabel, - )); + return $this->_oHtml->getLinkButton([ + 'href' => $sLink, + 'title' => $sHint, + 'class' => $sClass, + 'type' => $sFunction, + 'onmouseover' => $sOnMouseover, + 'onmouseout' => $sOnMouseout, + 'label' => $sLabel, + ]); // return '<a href="' . $sLink . '" ' . $sOnMouseover . ' title="' . $sHint . '" class="btn btn-default ' . $sClass . '"><i class="' . $sIconClass . '"></i> ' . $sLabel . '</a>'; } /** - * return html code for the setup form for a new project + * Get html code for the setup form for a new project * @return string */ - public function renderNewProject() { + public function renderNewProject(): string + { global $aParams; if (!$this->oUser->hasPermission("project-action-create")) { return $this->oUser->showDenied(); } - require_once ("formgen.class.php"); + require_once("formgen.class.php"); $i = 0; $sID = array_key_exists("id", $aParams) ? $aParams["id"] : ""; - $aForms = array( - 'setup' => array( - 'meta' => array( + $aForms = [ + 'setup' => [ + 'meta' => [ 'method' => 'POST', 'action' => '?', - ), - 'validate' => array(), - 'form' => array( - 'input' . $i++ => array( + ], + 'validate' => [], + 'form' => [ + 'input' . $i++ => [ 'type' => 'hidden', 'name' => 'setupaction', 'value' => 'create', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'text', 'name' => 'id', 'label' => t("class-project-info-setup-projectId"), @@ -472,29 +500,36 @@ class projectgui extends project { 'size' => 100, 'pattern' => '[a-z0-9\-_]*', 'placeholder' => t("class-project-info-setup-projectId-placeholder"), - ), - ), - ), - ); - $aForms["setup"]["form"]['input' . $i++] = array( + ], + ], + ], + ]; + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'submit', 'name' => 'btnsave', 'label' => t("save"), 'value' => $this->_oHtml->getIcon('sign-ok') . t("save"), - ); + ]; $oForm = new formgen($aForms); return $oForm->renderHtml("setup"); } + /** - * render html for a place of a phase + * Get html for a place of a phase. + * It returns false when + * - phase or place are empty + * - phase is not active + * - place is not valid + * * @param string $sPhase phase * @param string $sPlace name of the place; one of onhold|ready2install|deployed * @param bool $bActions draw action links (deploy, accept) on/ off * @param bool $bLong use long variant to display infos? * @return string|boolean */ - public function renderPhaseDetail($sPhase, $sPlace, $bActions = true, $bLong = true) { + public function renderPhaseDetail(string $sPhase, string $sPlace, bool $bActions = true, bool $bLong = true): bool|string + { if (!$sPhase) { return false; @@ -508,7 +543,7 @@ class projectgui extends project { if (!array_key_exists($sPlace, $this->_aPlaces)) { return false; } - + $sReturn = ''; $aDataPhase = $this->getPhaseInfos($sPhase); $aData = $aDataPhase[$sPlace]; @@ -532,37 +567,36 @@ class projectgui extends project { if ($bLong) { // long display of the revision // $sJsonUrl = $this->_getInfofile($sPhase, $sPlace); - $sReturn .=$this->_getChecksumDiv( - $aData["revision"], - $this->_oHtml->getIconByType('calendar') .' ' . date($sDateFormat, $oPkgDate) . '<br>' - . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData["branch"] . '<br>' - . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData["revision"]) . '<br>' - . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ':<br>' - ) + $sReturn .= $this->_getChecksumDiv( + $aData["revision"], + $this->_oHtml->getIconByType('calendar') . ' ' . date($sDateFormat, $oPkgDate) . '<br>' + . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData["branch"] . '<br>' + . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData["revision"]) . '<br>' + . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ':<br>' + ) . '<pre>' . $this->transformCommitMessage($aData["message"]) . '</pre>' - // . '<i class="glyphicon glyphicon-globe"></i> ' . t('url') . ': <a href="' . $sJsonUrl . '">' . $sJsonUrl . '</a><br>' ; if ($sPlace == "deployed" && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) { $sUrl = $this->_aPrjConfig["phases"][$sPhase]["url"]; - $sReturn.=$this->_oHtml->getIconByType('link-extern') . ' '. t('url') . ': <a href="' . $sUrl . '">' . $sUrl . '</a><br>'; + $sReturn .= $this->_oHtml->getIconByType('link-extern') . ' ' . t('url') . ': <a href="' . $sUrl . '">' . $sUrl . '</a><br>'; } } else { $sReturn .= $this->_getChecksumDiv( - $aData["revision"], - $this->_oHtml->getIconByType('calendar') .' ' . date($sDateFormat, $oPkgDate) - ); + $aData["revision"], + $this->_oHtml->getIconByType('calendar') . ' ' . date($sDateFormat, $oPkgDate) + ); if ($sPlace == "deployed" && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) { - $sMore = $this->_oHtml->getIconByType('link-extern').' ' - . t('url') - . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>'; + $sMore = $this->_oHtml->getIconByType('link-extern') . ' ' + . t('url') + . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>'; } - $sReturn.=' ' . $this->renderInfoLink( - $aData, - [ - 'title' => $this->getLabel() . " :: $sPhase :: $sPlace", - 'more' => $sMore, - ] + $sReturn .= ' ' . $this->renderInfoLink( + $aData, + [ + 'title' => $this->getLabel() . " :: $sPhase :: $sPlace", + 'more' => $sMore, + ] ); } @@ -571,9 +605,9 @@ class projectgui extends project { if (array_key_exists("phases", $this->_aConfig) && array_key_exists($sPhase, $this->_aConfig["phases"])) { // $sReturn .= print_r($this->_aConfig["phases"][$sPhase], true); if (count($this->_getDeploytimes($sPhase))) { - $sReturn .= '<br>'.$this->_oHtml->getIcon('time').t('deploytimes') . ':<br>' - . implode("<br>", array_values($this->_getDeploytimes($sPhase))) - . '<br>'; + $sReturn .= '<br>' . $this->_oHtml->getIcon('time') . t('deploytimes') . ':<br>' + . implode("<br>", array_values($this->_getDeploytimes($sPhase))) + . '<br>'; } if ($bActions) { $sReturn .= ' ' . $this->renderLink("deploy", $sPhase); @@ -601,13 +635,13 @@ class projectgui extends project { // $this->_getChecksumDiv($aData["revision"]) } else { if (array_key_exists("error", $aData)) { - $sReturn.='' - . $this->renderInfoLink(array('error' => $aData["error"]), array()) + $sReturn .= '' + . $this->renderInfoLink(['error' => $aData["error"], [] ]) ; } else if (array_key_exists("warning", $aData)) { - $sReturn.= '<div class="warning">'.$this->_oHtml->getIcon('sign-info'). t('warning') . ':<br>' . $aData["warning"] . '</div>'; + $sReturn .= '<div class="warning">' . $this->_oHtml->getIcon('sign-info') . t('warning') . ':<br>' . $aData["warning"] . '</div>'; } else { - + // OK = 1 ... for the queue we show no hint return ''; /* @@ -618,43 +652,44 @@ class projectgui extends project { */ } } // if - // } // for return $sReturn; } /** - * render html for the project overview; it shows the defined phases for + * Get html for the project overview; it shows the defined phases for * the project as a table - * @return type + * @return string */ - public function renderPhaseInfo() { + public function renderPhaseInfo(): string + { $sRow1 = false; $sRow2 = false; - $renderAdminLTE=new renderadminlte(); + $renderAdminLTE = new renderadminlte(); - $iWidth=min(12 / count($this->getActivePhases()), 4); + $iWidth = min(12 / count($this->getActivePhases()), 4); foreach ($this->getActivePhases() as $sPhase) { - $sRow1.=$renderAdminLTE->addCol( + $sRow1 .= $renderAdminLTE->addCol( '<table class="nomargin"><tr><th class="' . $sPhase . ' tdphase">' . $sPhase . '</th></tr></table>' , - $iWidth); - - $sDetails=t('url') . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>' - . '<br>' . t('deploytimes') . ':<br>'; + $iWidth + ); + + $sDetails = t('url') . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>' + . '<br>' . t('deploytimes') . ':<br>'; if (count($this->_getDeploytimes($sPhase))) { - $sDetails.=implode("<br>", $this->_getDeploytimes($sPhase)); + $sDetails .= implode("<br>", $this->_getDeploytimes($sPhase)); } else { - $sDetails.=t('deploytimes-immediately'); + $sDetails .= t('deploytimes-immediately'); } - $sDetails.='<br>' . $this->renderLink("phase", $sPhase) - . $this->_renderHosts($sPhase) - . '<br>' - . $this->_renderFiles($sPhase) - ; + $sDetails .= '<br>' . $this->renderLink("phase", $sPhase) + . $this->_renderHosts($sPhase) + . '<br>' + . $this->_renderFiles($sPhase) + ; - $sRow2.=$renderAdminLTE->addCol( - $renderAdminLTE->getCard(array ( + $sRow2 .= $renderAdminLTE->addCol( + $renderAdminLTE->getCard([ 'class' => $sPhase, 'variant' => '', 'tb-remove' => 1, @@ -663,106 +698,107 @@ class projectgui extends project { 'tools' => '', 'text' => $sDetails, 'footer' => '', - )), + ]), $iWidth ); } return '' - .$renderAdminLTE->addRow($sRow1) - .$renderAdminLTE->addRow($sRow2) - ; + . $renderAdminLTE->addRow($sRow1) + . $renderAdminLTE->addRow($sRow2) + ; } /** - * render html for a row with td for all places (first row) + * Get html for a row with td for all places (first row) * @param string $sPhase phase (just needed for coloring) * @return string */ - public function renderPlacesAsTd($sPhase) { + public function renderPlacesAsTd(string $sPhase): string + { $sRow1 = ''; foreach (array_keys($this->_aPlaces) as $sPlace) { - $sRow1.='<td class="' . $sPhase . ' ' . $this->_aConfig["id"] . ' tdphase">' . t($sPlace) . '</td>'; + $sRow1 .= '<td class="' . $sPhase . ' ' . $this->_aConfig["id"] . ' tdphase">' . t($sPlace) . '</td>'; } return $sRow1; } /** - * return html code for the setup form of an exsiting project + * Get html code for the setup form of an exsiting project * @return string */ - public function renderProjectSetup() { + public function renderProjectSetup(): string + { if (!$this->oUser->hasPermission("project-action-setup")) { return $this->oUser->showDenied(); } $sMessages = ''; - require_once ("formgen.class.php"); + require_once("formgen.class.php"); + $aSelectProjectGroup = [ + 'type' => 'select', + 'name' => 'projectgroup', + 'label' => t("projectgroup"), + 'options' => [ + OPTION_NONE => [ + 'label' => t('none'), + ], + '' => [ + 'label' => '- - - - - - - - - - - - - - - - - - - - ', + ], + ], + ]; + foreach ($this->_aConfig['projectgroups'] as $sGroupid => $sGroupLabel) { + $bActive = $this->getProjectGroup() === $sGroupid; + $aSelectProjectGroup['options'][$sGroupid] = [ + 'label' => $sGroupLabel, + 'selected' => $bActive ? 'selected' : false, + ]; + } - $aSelectProjectGroup = array( - 'type' => 'select', - 'name' => 'projectgroup', - 'label' => t("projectgroup"), - 'options' => array( - OPTION_NONE => array( - 'label' => t('none'), - ), - '' => array( - 'label' => '- - - - - - - - - - - - - - - - - - - - ', - ), - ), - ); - foreach($this->_aConfig['projectgroups'] as $sGroupid=>$sGroupLabel){ - $bActive=$this->getProjectGroup() === $sGroupid; - $aSelectProjectGroup['options'][$sGroupid] = array( - 'label' => $sGroupLabel, - 'selected' => $bActive ? 'selected' : false, - ); - } - - $aSelectSlack = array( - 'type' => 'hidden', - 'name' => 'messenger[slack]', - 'value' => false, - ); + $aSelectSlack = [ + 'type' => 'hidden', + 'name' => 'messenger[slack]', + 'value' => false, + ]; if ( - isset($this->_aConfig['messenger']['slack']['presets']) - && count($this->_aConfig['messenger']['slack']['presets']) + isset($this->_aConfig['messenger']['slack']['presets']) + && count($this->_aConfig['messenger']['slack']['presets']) ) { - $aSelectSlack = array( + $aSelectSlack = [ 'type' => 'select', 'name' => 'messenger[slack]', 'label' => t("messenger-slack"), - 'options' => array( - OPTION_NONE => array( + 'options' => [ + OPTION_NONE => [ 'label' => t('none'), - ), - '' => array( + ], + '' => [ 'label' => '- - - - - - - - - - - - - - - - - - - - ', - ), - ), - ); - foreach($this->_aConfig['messenger']['slack']['presets'] as $sSlackUrl=>$aSlackCfg){ - $bActive=$this->_aPrjConfig['messenger']['slack'] === $sSlackUrl; - $aSelectSlack['options'][$sSlackUrl] = array( + ], + ], + ]; + foreach ($this->_aConfig['messenger']['slack']['presets'] as $sSlackUrl => $aSlackCfg) { + $bActive = $this->_aPrjConfig['messenger']['slack'] === $sSlackUrl; + $aSelectSlack['options'][$sSlackUrl] = [ 'label' => array_key_exists('label', $aSlackCfg) ? $aSlackCfg['label'] : $sSlackUrl, 'selected' => $bActive ? 'selected' : false, - ); + ]; } - + } // ---------- Build plugins /* - $aPluginsBuild = array( - 'select' => array( + $aPluginsBuild = [ + 'select' => [ 'type' => 'checkbox', 'name' => 'build[enabled_build_plugins]', 'label' => t("build-plugins"), 'options' => [], - ), + ], // 'project-config' => '', - ); + ]; foreach (array_keys($this->getConfiguredPlugins('build')) as $sPluginName){ $sPluginFile=$this->_getPluginFilename('build', $sPluginName); @@ -773,20 +809,20 @@ class projectgui extends project { include_once $this->_getPluginFilename('build', $sPluginName); $TmpRolloutPlugin = new $sMyClassname([]); echo "FOUND $sMyClassname<br>"; - $aPluginsBuild['select']['options'][$sPluginName]=array( + $aPluginsBuild['select']['options'][$sPluginName]=[ 'label' => $TmpRolloutPlugin->getName(), 'checked' => $bActive, // 'onclick' => '$(\'.'.$sMyDivClass.'\').hide(); $(\'.' . $sMyDivClassActive . '\').show();', - ); + ]; } catch (Exception $ex) { } } else { - $aRollout['project-select']['options'][$sPluginName]=array( + $aRollout['project-select']['options'][$sPluginName]=[ 'label' => 'not found: <span class="error">' . $sMyClassname . '</span>', 'checked' => false, 'disabled' => "disabled", - ); + ]; } @@ -795,74 +831,74 @@ class projectgui extends project { */ // ---------- /Build plugins - + // ---------- Rollout plugins - $aRollout = array( - 'project-select' => array( + $aRollout = [ + 'project-select' => [ 'type' => 'radio', 'name' => 'deploy[enabled_rollout_plugin]', 'label' => t("deploy-rollout-plugin"), - ), + ], 'project-config' => '', - ); - foreach (array_keys($this->getConfiguredPlugins('rollout')) as $sPluginName){ + ]; + foreach (array_keys($this->getConfiguredPlugins('rollout')) as $sPluginName) { - $sPluginFile=$this->_getPluginFilename('rollout', $sPluginName); + $sPluginFile = $this->_getPluginFilename('rollout', $sPluginName); $TmpRolloutPlugin = false; - $sMyClassname='rollout_'. $sPluginName; - $sMyDivId='rollout-'. $sPluginName.'-config'; - $sMyDivClass='rolloutconfigdiv'; - $sMyDivClassActive='rolloutconfigdiv-'. $sPluginName; - $bActive=$sPluginName === $this->oRolloutPlugin->getId(); - - if(file_exists($sPluginFile)){ - try{ + $sMyClassname = 'rollout_' . $sPluginName; + $sMyDivId = 'rollout-' . $sPluginName . '-config'; + $sMyDivClass = 'rolloutconfigdiv'; + $sMyDivClassActive = 'rolloutconfigdiv-' . $sPluginName; + $bActive = $sPluginName === $this->oRolloutPlugin->getId(); + + if (file_exists($sPluginFile)) { + try { include_once $this->_getPluginFilename('rollout', $sPluginName); - $TmpRolloutPlugin = new $sMyClassname(array( - 'lang'=>$this->_aConfig['lang'], - 'phase'=>false, - 'globalcfg'=>$this->_aConfig['plugins']['rollout'][$sPluginName], - 'projectcfg'=>$this->_aPrjConfig, - )); - $aRollout['project-select']['options'][$sPluginName]=array( - 'label' => $TmpRolloutPlugin->getName(), - 'checked' => $bActive, - 'onclick' => '$(\'.'.$sMyDivClass.'\').hide(); $(\'.' . $sMyDivClassActive . '\').show();', - ); - - $aRollout['project-config'].='' - . '<div id="'.$sMyDivId.'" class="'.$sMyDivClass.' '.$sMyDivClassActive.'"' - . ($bActive ? '' : ' style="display: none;"' ) - . '>' - . $TmpRolloutPlugin->renderFormdata4Project() - . '</div>' - ; - + $TmpRolloutPlugin = new $sMyClassname([ + 'lang' => $this->_aConfig['lang'], + 'phase' => false, + 'globalcfg' => $this->_aConfig['plugins']['rollout'][$sPluginName], + 'projectcfg' => $this->_aPrjConfig, + ]); + $aRollout['project-select']['options'][$sPluginName] = [ + 'label' => $TmpRolloutPlugin->getName(), + 'checked' => $bActive, + 'onclick' => '$(\'.' . $sMyDivClass . '\').hide(); $(\'.' . $sMyDivClassActive . '\').show();', + ]; + + $aRollout['project-config'] .= '' + . '<div id="' . $sMyDivId . '" class="' . $sMyDivClass . ' ' . $sMyDivClassActive . '"' + . ($bActive ? '' : ' style="display: none;"') + . '>' + . $TmpRolloutPlugin->renderFormdata4Project() + . '</div>' + ; + // generate form firlds for each phase - foreach(array_keys($this->getPhases()) as $sMyPhase){ - $aRollout[$sMyPhase].='' - . '<div id="'.$sMyDivId.'-'.$sMyPhase.'" class="'.$sMyDivClass.' '.$sMyDivClassActive.'"' - . ($bActive ? '' : ' style="display: none;"' ) + foreach (array_keys($this->getPhases()) as $sMyPhase) { + $aRollout[$sMyPhase] .= '' + . '<div id="' . $sMyDivId . '-' . $sMyPhase . '" class="' . $sMyDivClass . ' ' . $sMyDivClassActive . '"' + . ($bActive ? '' : ' style="display: none;"') . '>' - . $TmpRolloutPlugin->renderFormdata4Phase($sMyPhase) + . $TmpRolloutPlugin->renderFormdata4Phase($sMyPhase) . '</div>' - ; + ; } } catch (Exception $ex) { } } else { - $aRollout['project-select']['options'][$sPluginName]=array( - 'label' => 'not found: <span class="error">' . $sMyClassname . '</span>', - 'checked' => false, - 'disabled' => "disabled", - ); + $aRollout['project-select']['options'][$sPluginName] = [ + 'label' => 'not found: <span class="error">' . $sMyClassname . '</span>', + 'checked' => false, + 'disabled' => "disabled", + ]; + - } } // ---------- /Rollout plugins - + $aForemanHostgroups = false; $iForemanHostgroupDefault = false; $sForemanHostgroupDefault = false; @@ -874,35 +910,36 @@ class projectgui extends project { // $oForeman->setDebug(1); // $oForeman->selfcheck(); die(__FUNCTION__); - $aForemanHostgroups = $oForeman->read(array( - 'request' => array( - array('hostgroups'), - // array('operatingsystems',4), - ), - 'response' => array( - 'id', 'title' - ), - )); - $aSelectForemanGroups = array( + $aForemanHostgroups = $oForeman->read([ + 'request' => [ + ['hostgroups'], + // ['operatingsystems',4], + ], + 'response' => [ + 'id', + 'title' + ], + ]); + $aSelectForemanGroups = [ 'type' => 'select', 'name' => 'deploy[foreman][hostgroup]', 'label' => $this->_oHtml->getIcon('foreman') . t("foreman-hostgroup"), - 'options' => array( - OPTION_NONE => array( + 'options' => [ + OPTION_NONE => [ 'label' => t('none'), - ), - '' => array( + ], + '' => [ 'label' => '- - - - - - - - - - - - - - - - - - - - ', - ), - ), - ); + ], + ], + ]; if ($aForemanHostgroups && count($aForemanHostgroups)) { foreach ($aForemanHostgroups as $aItem) { - $bActive=$iForemanHostgroupDefault === (int) $aItem['id']; - $aSelectForemanGroups['options'][$aItem['id']] = array( + $bActive = $iForemanHostgroupDefault === (int) $aItem['id']; + $aSelectForemanGroups['options'][$aItem['id']] = [ 'label' => $aItem['title'], 'selected' => $bActive ? 'selected' : false, - ); + ]; $sForemanHostgroupDefault = $bActive ? $aItem['title'] : $sForemanHostgroupDefault; } } @@ -912,87 +949,87 @@ class projectgui extends project { $i = 0; $aPrefixItem = count($this->getVersions()) ? - array( - 'type' => 'markup', - 'value' => '<div class="form-group"> + [ + 'type' => 'markup', + 'value' => '<div class="form-group"> <label class="col-sm-2">' . t('fileprefix') . '</label> <div class="col-sm-10"> <input id="inputprefix" type="hidden" name="fileprefix" value="' . $this->_aPrjConfig["fileprefix"] . '"> ' . $this->_aPrjConfig["fileprefix"] . ' </div></div> ', - ) : array( - 'type' => 'text', - 'name' => 'fileprefix', - // 'disabled' => 'disabled', - 'label' => t('fileprefix-label'), - 'value' => $this->_aPrjConfig["fileprefix"], - 'required' => 'required', - 'validate' => 'isastring', - 'pattern' => '[a-z0-9\-_]*', - 'size' => 100, - 'placeholder' => '', - ); + ] : [ + 'type' => 'text', + 'name' => 'fileprefix', + // 'disabled' => 'disabled', + 'label' => t('fileprefix-label'), + 'value' => $this->_aPrjConfig["fileprefix"], + 'required' => 'required', + 'validate' => 'isastring', + 'pattern' => '[a-z0-9\-_]*', + 'size' => 100, + 'placeholder' => '', + ]; // detect access to repo url - $aBranches=$this->getRemoteBranches(true); + $aBranches = $this->getRemoteBranches(true); // $aRepodata = $this->getRepoRevision(); // if (is_array($aRepodata) && array_key_exists("message", $aRepodata)) { if (is_array($aBranches) && count($aBranches)) { $sRepoCheck = '<span class="ok">' . sprintf(t('class-project-info-repoaccess'), count($aBranches)) . '</span>'; } else { - $sRepoError=sprintf(t('class-project-error-no-repoaccess'), $aRepodata["error"]); + $sRepoError = sprintf(t('class-project-error-no-repoaccess'), $aRepodata["error"]); $sRepoCheck = '<span class="error">' . $sRepoError . '</span>'; - $sMessages.=$this->_oHtml->getBox("error", $sRepoError); + $sMessages .= $this->_oHtml->getBox("error", $sRepoError); } // generate datalist with exisating ssh keys for auth field $sAuthListitems = ''; foreach ($this->_getSshKeys() as $sKey) { - $sAuthListitems.='<option value="' . $sKey . '">'; + $sAuthListitems .= '<option value="' . $sKey . '">'; } - $aForms = array( - 'setup' => array( - 'meta' => array( + $aForms = [ + 'setup' => [ + 'meta' => [ 'method' => 'POST', 'action' => '?', - ), - 'validate' => array(), - 'form' => array( - 'input' . $i++ => array( + ], + 'validate' => [], + 'form' => [ + 'input' . $i++ => [ 'type' => 'hidden', 'name' => 'setupaction', 'value' => 'save', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'hidden', 'name' => 'id', 'value' => $this->_aConfig["id"], - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'markup', 'value' => '<div class="tabbable"> <ul class="nav nav-tabs"> - <li class="active"><a href="#tab1" class="nav-link active" data-toggle="tab" aria-selected="1">' . $this->_oHtml->getIcon('list').t('setup-metadata') . '</a></li> - <li><a href="#tab2" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('repository').t('repositoryinfos') . '</a></li> + <li class="active"><a href="#tab1" class="nav-link active" data-toggle="tab" aria-selected="1">' . $this->_oHtml->getIcon('list') . t('setup-metadata') . '</a></li> + <li><a href="#tab2" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('repository') . t('repositoryinfos') . '</a></li> - <li><a href="#tab3" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-configfile').t('deploy-configfile') . '</a></li> - <li><a href="#tab4" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-rollout-plugin').t('deploy-rollout-plugin') . '</a></li> - <li><a href="#tab5" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('phase').t('phases') . '</a></li> - <li><a href="#tab6" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('raw-data').t('raw-data') . '</a></li> + <li><a href="#tab3" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-configfile') . t('deploy-configfile') . '</a></li> + <li><a href="#tab4" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-rollout-plugin') . t('deploy-rollout-plugin') . '</a></li> + <li><a href="#tab5" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('phase') . t('phases') . '</a></li> + <li><a href="#tab6" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('raw-data') . t('raw-data') . '</a></li> </ul> <div class="tab-content"> <div class="tab-pane fade active show" id="tab1"> <br> ', - ), + ], // -------------------------------------------------- // Tab for metadata // ------------------------------------------------- - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'text', 'name' => 'label', 'label' => t('projectname'), @@ -1001,8 +1038,8 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => 'Projekt', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'text', 'name' => 'description', 'label' => t('projectdescription'), @@ -1011,8 +1048,8 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => '', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'text', 'name' => 'contact', 'label' => t('contact'), @@ -1021,15 +1058,15 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => '', - ), + ], 'input' . $i++ => $aSelectProjectGroup, - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'markup', 'value' => '<p>' . t('messenger') . '</p>', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'text', 'name' => 'messenger[email]', 'label' => t("messenger-email"), @@ -1038,19 +1075,19 @@ class projectgui extends project { 'size' => 100, 'placeholder' => '', 'autocomplete' => 'off', - ), - + ], + 'input' . $i++ => $aSelectSlack, - + // -------------------------------------------------- // Tab soources repository & build // -------------------------------------------------- - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'markup', 'value' => ' </div><div class="tab-pane fade" id="tab2"> <p>' . t('setup-hint-build') . '</p>', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'text', 'name' => 'build[type]', 'label' => t("build-type"), @@ -1059,8 +1096,8 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => '', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'text', 'name' => 'build[url]', 'label' => t("repository-url"), @@ -1069,8 +1106,8 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => '', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'text', 'name' => 'build[auth]', 'label' => t("repository-auth"), @@ -1080,19 +1117,19 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => '', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'markup', 'value' => '<datalist id="listauth">' . $sAuthListitems . '</datalist>', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'markup', 'value' => '<div class="form-group">' - . '<label class="col-sm-2"> </label><div class="col-sm-10">' - . $sRepoCheck - . '</div></div>', - ), - 'input' . $i++ => array( + . '<label class="col-sm-2"> </label><div class="col-sm-10">' + . $sRepoCheck + . '</div></div>', + ], + 'input' . $i++ => [ 'type' => 'text', 'name' => 'build[webaccess]', 'label' => t("repository-urlwebgui"), @@ -1100,48 +1137,48 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => '', - ), + ], 'input' . $i++ => $aPrefixItem, - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'markup', 'value' => '<div style="clear: both"></div>', - ), + ], // task#1498 - handle project without "public" directory - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'checkbox', 'name' => 'build[haspublic]', 'label' => t("repository-has-public-dir"), 'required' => false, 'validate' => 'isastring', - 'options' => array( - '1' => array( + 'options' => [ + '1' => [ 'label' => t("yes"), 'checked' => (array_key_exists('haspublic', $this->_aPrjConfig["build"]) ? $this->_aPrjConfig["build"]["haspublic"] : 0), - ), - ), - ), + ], + ], + ], // -------------------------------------------------- // Tab for config and API key // -------------------------------------------------- - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'markup', 'value' => ' </div><div class="tab-pane fade" id="tab3"> <p>' . t('deploy-configfile-hint') . '</p>', - ), - 'textarea' . $i++ => array( + ], + 'textarea' . $i++ => [ 'type' => 'textarea', 'name' => 'deploy[configfile]', 'label' => t("deploy-configfile"), 'value' => $this->_aPrjConfig['deploy']["configfile"], // 'required' => 'required', - 'validate' => 'isastring', + 'validate' => 'isastring', 'cols' => 100, 'rows' => 10, 'placeholder' => 'export myvariable="hello world"', - ), - - 'input' . $i++ => array( + ], + + 'input' . $i++ => [ 'type' => 'text', 'name' => 'api[secret]', 'label' => t("api-secret"), @@ -1149,56 +1186,56 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => '', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'markup', 'value' => '<div class="col-sm-12">' - . '<p>' . t('api-secret-hint') . '<br>' - . '<a href="#" class="btn btn-default" onclick="$(\'#input'.($i-2).'\').val(generateSecret(64)); return false">'.t("api-secret-generate").'</a>' - . '</p></div>', - ), - + . '<p>' . t('api-secret-hint') . '<br>' + . '<a href="#" class="btn btn-default" onclick="$(\'#input' . ($i - 2) . '\').val(generateSecret(64)); return false">' . t("api-secret-generate") . '</a>' + . '</p></div>', + ], + // -------------------------------------------------- // Tab rollout plugin // -------------------------------------------------- - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'markup', 'value' => ' </div><div class="tab-pane fade" id="tab4"> <p>' . t('deploy-rollout-plugin-hint') . '</p>', - ), + ], // select box for active rollout plugin $aRollout['project-select'], - + // project based config - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'markup', 'value' => '' . '<hr>' - .'<label class="col-sm-2">'.t('deploy-rollout-plugin-config') .'</label>' - .'<div class="col-sm-10">'. $aRollout['project-config'].'</div>' - ), + . '<label class="col-sm-2">' . t('deploy-rollout-plugin-config') . '</label>' + . '<div class="col-sm-10">' . $aRollout['project-config'] . '</div>' + ], // -------------------------------------------------- - 'input' . $i++ => array( + 'input' . $i++ => [ 'type' => 'markup', 'value' => ' </div><div class="tab-pane fade" id="tab5"> <p>' . sprintf(t("class-project-info-setup-phaseinfos"), $this->getNextPhase()) . '</p>', - ), - ), - ), - ); + ], + ], + ], + ]; // -------------------------------------------------- // Tab for phases // -------------------------------------------------- if ($aSelectForemanGroups) { - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', - 'value' => '<strong>'.t("defaults-all-phases").'</strong><br><br>', - ); + 'value' => '<strong>' . t("defaults-all-phases") . '</strong><br><br>', + ]; $aForms["setup"]["form"]['input' . $i++] = $aSelectForemanGroups; - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '<br><br>', - ); + ]; } foreach (array_keys($this->getPhases()) as $sPhase) { @@ -1227,50 +1264,50 @@ class projectgui extends project { if ($aSelectForemanGroups) { $iForemanHostgroup = (int) $this->_aPrjConfig['phases'][$sPhase]['foreman-hostgroup']; - $aSelectForemanHostGroup = array( + $aSelectForemanHostGroup = [ 'type' => 'select', 'name' => 'phases[' . $sPhase . '][foreman-hostgroup]', 'label' => $this->_oHtml->getIcon('foreman') . t("foreman-hostgroup"), - 'options' => array( - OPTION_DEFAULT => array( + 'options' => [ + OPTION_DEFAULT => [ 'label' => t('default') . ' (' . $sForemanHostgroupDefault . ')', 'selected' => $iForemanHostgroup === OPTION_DEFAULT ? 'selected' : false, - ), - OPTION_NONE => array( + ], + OPTION_NONE => [ 'label' => t('none'), 'selected' => $iForemanHostgroup === OPTION_NONE ? 'selected' : false, - ), - '' => array( + ], + '' => [ 'label' => '- - - - - - - - - - - - - - - - - - - - ', - ), - ), - ); + ], + ], + ]; if (is_array($aForemanHostgroups) && count($aForemanHostgroups)) { foreach ($aForemanHostgroups as $aItem) { - $aSelectForemanHostGroup['options'][$aItem['id']] = array( + $aSelectForemanHostGroup['options'][$aItem['id']] = [ 'label' => $aItem['title'], 'selected' => ($iForemanHostgroup === $aItem['id']) ? 'selected' : false, - ); + ]; } } } - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '' - // .'<pre>'.print_r($this->_aPrjConfig["phases"][$sPhase], 1).'</pre>' - /* - . '<a class="'.$sPhase.'">' - . t("phase") . ' ' . $sPhase - . '</a>' - */ - . '<table class="table">' - . '<tbody>' - . '<tr><th class="' . $sPhase . '">' . $this->_oHtml->getIcon('phase') . t("phase") . ' ' . $sPhase . '</th></tr>' - . '<tr><td class="' . ($bActivePhase ? $sPhase : '') . '">' - . '' - ); + // .'<pre>'.print_r($this->_aPrjConfig["phases"][$sPhase], 1).'</pre>' + /* + . '<a class="'.$sPhase.'">' + . t("phase") . ' ' . $sPhase + . '</a>' + */ + . '<table class="table">' + . '<tbody>' + . '<tr><th class="' . $sPhase . '">' . $this->_oHtml->getIcon('phase') . t("phase") . ' ' . $sPhase . '</th></tr>' + . '<tr><td class="' . ($bActivePhase ? $sPhase : '') . '">' + . '' + ]; - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'checkbox', 'name' => 'phases[' . $sPhase . '][active]', 'label' => t("phase-is-active"), @@ -1279,20 +1316,20 @@ class projectgui extends project { 'validate' => 'isastring', // 'size' => 100, // 'placeholder' => '...', - 'options' => array( - '1' => array( + 'options' => [ + '1' => [ 'label' => t("yes"), 'checked' => $bActivePhase, 'onclick' => '$(\'#' . $sDivId4PhaseSettings . '\').css(\'display\', (this.checked ? \'block\' : \'none\') )', - ), - ), - ); - $aForms["setup"]["form"]['input' . $i++] = array( + ], + ], + ]; + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '' - . '<div id="' . $sDivId4PhaseSettings . '" ' . ($bActivePhase ? '' : ' style="display: none;"') . '>' - ); - $aForms["setup"]["form"]['input' . $i++] = array( + . '<div id="' . $sDivId4PhaseSettings . '" ' . ($bActivePhase ? '' : ' style="display: none;"') . '>' + ]; + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'text', 'name' => 'phases[' . $sPhase . '][url]', 'label' => $this->_oHtml->getIcon('url') . t("url-project-website"), @@ -1301,8 +1338,8 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => 'https://' . $sPhase . '.[' . t("project") . '].[...]/', - ); - $aForms["setup"]["form"]['input' . $i++] = array( + ]; + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'radio', 'name' => 'phases[' . $sPhase . '][deploymethod]', 'label' => $this->_oHtml->getIcon('method') . t("deploymethod"), @@ -1311,46 +1348,46 @@ class projectgui extends project { 'validate' => 'isastring', // 'size' => 100, // 'placeholder' => '...', - 'options' => array( - 'none' => array( + 'options' => [ + 'none' => [ 'label' => t("deploymethod-none"), 'checked' => $sDeploymethod === "none", 'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'none\' : \'block\') )', - ), - 'rolloutplugin' => array( + ], + 'rolloutplugin' => [ // 'label' => t("deploymethod-puppet").' - '. $this->oRolloutPlugin->getName(), 'label' => t("deploymethod-rolloutplugin"), 'checked' => $sDeploymethod === "rolloutplugin", 'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'block\' : \'none\') )', - ), - /* - * see deploy method to handle an action - 'sshproxy' => array( - 'label' => t("deploymethod-sshproxy"), - 'checked' => $sDeploymethod==="sshproxy", - 'onclick' => '$(\'#'.$sDivId4TargetHosts.'\').css(\'display\', (this.checked ? \'block\' : \'none\') )', - ), - */ - ), - ); - + ], + /* + * see deploy method to handle an action + 'sshproxy' => [ + 'label' => t("deploymethod-sshproxy"), + 'checked' => $sDeploymethod==="sshproxy", + 'onclick' => '$(\'#'.$sDivId4TargetHosts.'\').css(\'display\', (this.checked ? \'block\' : \'none\') )', + ], + */ + ], + ]; + - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '' - . '<div id="' . $sDivId4TargetHosts . '" ' . ($sDeploymethod !== "none" ? '' : ' style="display: none;"') . '>' - ); - + . '<div id="' . $sDivId4TargetHosts . '" ' . ($sDeploymethod !== "none" ? '' : ' style="display: none;"') . '>' + ]; + // rollout plugin: phase specific overrides - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '' // . '<hr>' - .'<label class="col-sm-2">'.t('deploy-rollout-plugin-config') .'</label>' - .'<div class="col-sm-10">'.$aRollout[$sPhase].'</div>' - ); - - $aForms["setup"]["form"]['input' . $i++] = array( + . '<label class="col-sm-2">' . t('deploy-rollout-plugin-config') . '</label>' + . '<div class="col-sm-10">' . $aRollout[$sPhase] . '</div>' + ]; + + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'text', 'name' => 'phases[' . $sPhase . '][hosts]', 'label' => $this->_oHtml->getIcon('host') . t("phase-targethosts"), @@ -1359,7 +1396,7 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => 'FQDN1,FQDN2', - ); + ]; /* if ($sPuppethost) { @@ -1379,18 +1416,18 @@ class projectgui extends project { } else { $sOut = '<span class="ok">' . sprintf(t("class-project-info-setup-ssh-and-puppet-ok"), $sPuppethost) . '</span>'; } - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '<div class="form-group">' . '<label class="col-sm-2"> </label><div class="col-sm-10">' . $sOut . '</div></div>', - ); + ]; } */ // when to deploy - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'text', 'name' => 'phases[' . $sPhase . '][deploytimes]', 'label' => $this->_oHtml->getIcon('time') . t("deploytimes"), @@ -1399,92 +1436,94 @@ class projectgui extends project { 'validate' => 'isastring', 'size' => 100, 'placeholder' => isset($this->_aConfig["phases"][$sPhase]["deploytimes"]) ? implode(", ", $this->_aConfig["phases"][$sPhase]["deploytimes"]) : '', - ); - $aForms["setup"]["form"]['input' . $i++] = array( + ]; + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '' - . '</div>' - ); + . '</div>' + ]; if ($aSelectForemanGroups) { $aForms["setup"]["form"]['input' . $i++] = $aSelectForemanHostGroup; } - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '' - . '</div>' - ); // close div for active phase + . '</div>' + ]; // close div for active phase - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '</td></tr></tbody></table>', - ); + ]; } // END: loop over phases // -------------------------------------------------- // Tab for raw data // -------------------------------------------------- - - $sRolloutDebug='<hr>DEBUG:<br>'; + + $sRolloutDebug = '<hr>DEBUG:<br>'; foreach (array_keys($this->getPhases()) as $sPhase) { - if ($this->isActivePhase($sPhase)){ - $sRolloutDebug.='<strong>'.$sPhase.'</strong>' - . '<pre>Config = '.print_r($this->oRolloutPlugin->getConfig($sPhase, 1), 1).'</pre>' - . '<pre>Commands = '.print_r($this->oRolloutPlugin->getDeployCommands($sPhase, 1), 1).'</pre>' + if ($this->isActivePhase($sPhase)) { + $sRolloutDebug .= '<strong>' . $sPhase . '</strong>' + . '<pre>Config = ' . print_r($this->oRolloutPlugin->getConfig($sPhase, 1), 1) . '</pre>' + . '<pre>Commands = ' . print_r($this->oRolloutPlugin->getDeployCommands($sPhase, 1), 1) . '</pre>' ; } } - $aForms["setup"]["form"]['input' . $i++] = array( + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'markup', 'value' => '</div>' - + . '<div class="tab-pane fade" id="tab6">' - . '<br><pre>'.print_r($this->_aPrjConfig, 1).'</pre>' + . '<br><pre>' . print_r($this->_aPrjConfig, 1) . '</pre>' . $sRolloutDebug . '</div>' - - . '</div>' - . '</div>' - . '<div style="clear: both; margin-bottom: 1em;"></div>' - - - . '<hr>', - ); - $aForms["setup"]["form"]['input' . $i++] = array( + + . '</div>' + . '</div>' + . '<div style="clear: both; margin-bottom: 1em;"></div>' + + + . '<hr>', + ]; + $aForms["setup"]["form"]['input' . $i++] = [ 'type' => 'submit', 'name' => 'btnsave', 'label' => t("save"), - 'value' => $this->_oHtml->getIcon('sign-ok').t("save"), - ); + 'value' => $this->_oHtml->getIcon('sign-ok') . t("save"), + ]; $oForm = new formgen($aForms); return $sMessages . $oForm->renderHtml("setup"); } /** - * return html code for the installed version in the repository + * Get html code for the installed version in the repository * @param boolean $bRefresh optional: refresh flag; default: use cached information * @return string */ - public function renderRepoInfo($bRefresh=false) { + public function renderRepoInfo(bool $bRefresh = false): string + { $sReturn = ""; switch ($this->_aPrjConfig["build"]["type"]) { case "git": $aRepodata = $this->getRepoRevision($bRefresh); if (array_key_exists("revision", $aRepodata)) { - $sReturn.=$this->_getChecksumDiv($aRepodata["revision"], + $sReturn .= $this->_getChecksumDiv( + $aRepodata["revision"], $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . (array_key_exists("branch", $aRepodata) ? $aRepodata["branch"] : '-') . '<br>' . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aRepodata["revision"]) . '<br>' . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ':<br>' - ) - ."<pre>" . htmlentities($aRepodata["message"]). "</pre>"; + ) + . "<pre>" . htmlentities($aRepodata["message"]) . "</pre>"; } else { $sReturn .= $this->_oHtml->getBox("error", sprintf(t('class-project-error-no-repoinfo'), $aRepodata["error"])) - . $this->renderLink("setup") . '<br>'; + . $this->renderLink("setup") . '<br>'; } break; @@ -1493,36 +1532,40 @@ class projectgui extends project { $sReturn .= $this->_oHtml->getBox("error", sprintf(t('class-project-error-wrong-buildtype'), $this->_aPrjConfig["build"]["type"])); } if (array_key_exists("url", $this->_aPrjConfig["build"])) { - $sReturn.=t('repository-url') . ': ' . $this->_aPrjConfig["build"]["url"] . '<br>'; + $sReturn .= t('repository-url') . ': ' . $this->_aPrjConfig["build"]["url"] . '<br>'; } if (array_key_exists("webaccess", $this->_aPrjConfig["build"])) { - $sReturn.=t('repository-access-browser') . ':<br><a href="' . $this->_aPrjConfig["build"]["webaccess"] . '">' . $this->_aPrjConfig["build"]["webaccess"] . '</a><br>'; + $sReturn .= t('repository-access-browser') . ':<br><a href="' . $this->_aPrjConfig["build"]["webaccess"] . '">' . $this->_aPrjConfig["build"]["webaccess"] . '</a><br>'; } return $sReturn; } /** - * get html code for a link to the commit + * Get html code for a link to the commit * (works for guithub and gitlab instances) * - * @param string $sRevision + * @param null|string $sRevision * @return string */ - public function _renderRevision($sRevision) { + public function _renderRevision(null|string $sRevision): string + { $sUrl = str_replace('/tree/master', '', $this->_aPrjConfig["build"]["webaccess"]) . '/commit/' . $sRevision; return '<a href="' . $sUrl . '">' . $sRevision . '</a>'; - return $sUrl; + // return $sUrl; } + /** - * get html form with selectr for remote branches + * Get html form with selectr for remote branches + * * @param string $sActiveBranchname force active branch name * @param bool $bIgnoreCache flag to ignore exiting cached data * @return string */ - public function renderSelectRemoteBranches($sActiveBranchname = false, $bIgnoreCache=false) { - $this->log(__FUNCTION__."(sActiveBranchname = $sActiveBranchname, bIgnoreCache = ".($bIgnoreCache ? 'true' : 'false').") start"); - $aReturn = array(); - $aRadios = array(); + public function renderSelectRemoteBranches(string $sActiveBranchname = '', bool $bIgnoreCache = false): string + { + $this->log(__FUNCTION__ . "(sActiveBranchname = $sActiveBranchname, bIgnoreCache = " . ($bIgnoreCache ? 'true' : 'false') . ") start"); + $aReturn = []; + $aRadios = []; $bFoundActive = false; $i = 0; if (!$this->_oVcs) { @@ -1539,10 +1582,10 @@ class projectgui extends project { } foreach ($this->_oVcs->getRemoteBranches($bIgnoreCache) as $aBranch) { $sBranch = $aBranch['name']; - $aRadios[$sBranch] = array( + $aRadios[$sBranch] = [ 'value' => $sBranch, 'label' => $aBranch['label'], - ); + ]; // if no param was given the first branch will be marked if (!$sActiveBranchname) { $sActiveBranchname = $sBranch; @@ -1556,23 +1599,24 @@ class projectgui extends project { // not on the option (Chrome) // $aRadios[$sBranch]['onclick'] = 'document.getElementById(\'submitBranch\').click()'; } - }; + } + ; } // no branches were found if (count($aRadios) == 0) { return ''; } - $aForms = array( - 'frmSelectBranch' => array( - 'meta' => array( + $aForms = [ + 'frmSelectBranch' => [ + 'meta' => [ 'method' => 'POST', 'action' => '?', 'id' => 'frmSelectBranch', - ), - 'validate' => array(), - 'form' => array( - 'branchname' => array( + ], + 'validate' => [], + 'form' => [ + 'branchname' => [ 'inline' => true, 'type' => 'select', 'onchange' => 'document.getElementById(\'submitBranch\').click()', @@ -1580,33 +1624,34 @@ class projectgui extends project { 'label' => '<strong>' . t('branch-select') . '</strong>', 'validate' => 'isastring', 'options' => $aRadios, - ), - ), - ), - ); + ], + ], + ], + ]; // submit to switch branches - only if a selection is available if (count($aRadios) > 1 || !$bFoundActive) { - $aForms['frmSelectBranch']['form']['submitBranch'] = array( + $aForms['frmSelectBranch']['form']['submitBranch'] = [ 'type' => 'submit', 'name' => 'btnsave', 'onclick' => 'showModalMessage(\'' . t('branch-switch') . '\'); ', 'label' => t("change"), - 'value' => $this->_oHtml->getIcon('sign-ok').t("change"), - ); + 'value' => $this->_oHtml->getIcon('sign-ok') . t("change"), + ]; } $oFrm = new formgen($aForms); return $oFrm->renderHtml('frmSelectBranch') - . '<script>$("#submitBranch").hide();</script>'; + . '<script>$("#submitBranch").hide();</script>'; // return $oFrm->renderHtmlElement('dummy',$aFormData); } /** - * return html code for a list of all built packages and their usage + * Get html code for a list of all built packages and their usage * @return string */ - public function renderVersionUsage() { + public function renderVersionUsage(): string + { $sReturn = false; $sRowHead1 = false; $sRowHead2 = '<td></td>'; @@ -1617,26 +1662,26 @@ class projectgui extends project { } foreach ($this->getActivePhases() as $sPhase) { - $sRowHead1.='<th class="' . $sPhase . ' tdphase" colspan="' . (count($this->_aPlaces) + 1) . '">' . $sPhase . '</th>'; - $sRowHead2.='<td></td>' . $this->renderPlacesAsTd($sPhase); + $sRowHead1 .= '<th class="' . $sPhase . ' tdphase" colspan="' . (count($this->_aPlaces) + 1) . '">' . $sPhase . '</th>'; + $sRowHead2 .= '<td></td>' . $this->renderPlacesAsTd($sPhase); } - + krsort($aAllVersions); foreach ($aAllVersions as $sVersion => $aData) { - $sReturn.='<tr>'; - - $sInfos = $this->renderInfoLink($aData["info"], array('hpos' => 'left')); - $sReturn.='<td>' - . $this->_getChecksumDiv( - $aData['info']['revision'], - $this->_oHtml->getIconByType('calendar') . t('build-from') . ': ' . $sVersion .'<br>' - . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData['info']["branch"] . '<br>' - . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData['info']["revision"]) . '<br>' - ) - . '</td><td>' - . ' ' . $sInfos . ' ' - . '</td>' - ; + $sReturn .= '<tr>'; + + $sInfos = $this->renderInfoLink($aData["info"], ['hpos' => 'left']); + $sReturn .= '<td>' + . $this->_getChecksumDiv( + $aData['info']['revision'], + $this->_oHtml->getIconByType('calendar') . t('build-from') . ': ' . $sVersion . '<br>' + . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData['info']["branch"] . '<br>' + . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData['info']["revision"]) . '<br>' + ) + . '</td><td>' + . ' ' . $sInfos . ' ' + . '</td>' + ; foreach ($this->getActivePhases() as $sPhase) { $sTLine = ''; @@ -1644,51 +1689,52 @@ class projectgui extends project { // $sReturn.=$aData["rollback"][$sPhase] ? '<td>'.$this->renderLink("rollback", $sPhase, $sVersion).'</td>' : '<td>Rollback NOT possible</td>'; // $sReturn.=$aData["rollback"][$sPhase] ? '<td> Y </td>' : '<td> N </td>'; - $sReturn.='<td> </td>'; + $sReturn .= '<td> </td>'; foreach (array_keys($this->_aPlaces) as $sPlace) { $bFound = false; - $sReturn.=$aData["usage"][$sPhase][$sPlace] - ? '<td class="' . $sPhase . '" style="text-align: center;">' - . $this->_getChecksumDiv($aData['info']['revision'], 'X') - . '</td>' - : '<td> </td>' - ; + $sReturn .= $aData["usage"][$sPhase][$sPlace] + ? '<td class="' . $sPhase . '" style="text-align: center;">' + . $this->_getChecksumDiv($aData['info']['revision'], 'X') + . '</td>' + : '<td> </td>' + ; } } - $sReturn.='</tr>'; + $sReturn .= '</tr>'; } $sReturn = t('class-project-info-table-packages') . '<br><br>' - . '<table>' - . '<thead><tr><td>Version</td><td></td>' - . $sRowHead1 - . '</tr><tr><td>' - . $sRowHead2 - . '</tr></thead>' - . '<tbody>' - . $sReturn - . '</tbody>' - . '</table>'; + . '<table>' + . '<thead><tr><td>Version</td><td></td>' + . $sRowHead1 + . '</tr><tr><td>' + . $sRowHead2 + . '</tr></thead>' + . '<tbody>' + . $sReturn + . '</tbody>' + . '</table>'; return $sReturn; } /** - * render graphical overview of process (in project overview) + * Get html code for graphical overview of process (in project overview) * @return string */ - public function renderVisual() { + public function renderVisual(): string + { $sReturn = ''; - $renderAdminLTE=new renderadminlte(); + $renderAdminLTE = new renderadminlte(); $sBarHeightBg = '1.0em'; $sBarHeight = '0.8em'; $sContinue = '<span style="font-size: 300%; color:#ace;">»»</span><br>'; - $aBranches=$this->getRemoteBranches(); - if(!is_array($aBranches)){ - return '<br>'.$this->_oHtml->getBox("error", t("project-setup-incomplete")); + $aBranches = $this->getRemoteBranches(); + if (!is_array($aBranches)) { + return '<br>' . $this->_oHtml->getBox("error", t("project-setup-incomplete")); } $sRepoBar = ''; @@ -1706,8 +1752,8 @@ class projectgui extends project { $sPackagebar = ''; $aVersions = $this->_getVersionUsage(); foreach ($aVersions as $sVersion => $aData) { - $sBar = $aData["info"]["revision"] ? $this->_getChecksumDiv($aData["info"]["revision"], '' , $sBarHeight) : ''; - $sPackagebar.='<span title="' . $sVersion . '" style="float: left; background:#eee; height: '.$sBarHeightBg.'; width:' . (100 / count($aVersions)) . '%">' . $sBar . ' </span>'; + $sBar = $aData["info"]["revision"] ? $this->_getChecksumDiv($aData["info"]["revision"], '', $sBarHeight) : ''; + $sPackagebar .= '<span title="' . $sVersion . '" style="float: left; background:#eee; height: ' . $sBarHeightBg . '; width:' . (100 / count($aVersions)) . '%">' . $sBar . ' </span>'; } $sPhaseImg = ''; @@ -1718,18 +1764,18 @@ class projectgui extends project { if ($this->canAcceptPhase($sLastPhase)) { $sAction .= $this->renderLink("accept", $sLastPhase); } - $sPhaseImg.='<div class="action">' . $sAction . '</div>'; + $sPhaseImg .= '<div class="action">' . $sAction . '</div>'; } $sLastPhase = $sPhase; $sFullbar = ''; foreach (array_keys($this->_aPlaces) as $sPlace) { - $sFullbar.='<span title="' . $this->_aPlaces[$sPlace] . '" style="float: left; background:#eee; height: '.$sBarHeightBg.'; width:' . (100 / count($this->_aPlaces)) . '%">' . $this->_renderBar($sPhase, $sPlace, $sBarHeight) . ' </span>'; + $sFullbar .= '<span title="' . $this->_aPlaces[$sPlace] . '" style="float: left; background:#eee; height: ' . $sBarHeightBg . '; width:' . (100 / count($this->_aPlaces)) . '%">' . $this->_renderBar($sPhase, $sPlace, $sBarHeight) . ' </span>'; } // $sDetail = $sFullbar . '<br><a href="#h3phases" class="scroll-link">' . $sPhase . '</a>'; $sDetail = $sFullbar . '<br>' . $sPhase; - $sPhaseImg.=' + $sPhaseImg .= ' <div class="process"> <div class="details">' . $sDetail . ' </div> </div>'; @@ -1740,55 +1786,61 @@ class projectgui extends project { . $renderAdminLTE->addRow( '' . $renderAdminLTE->addCol( - $renderAdminLTE->getCard([ - 'type'=>'', - 'variant'=>'outline', - 'title'=>'', - 'text'=> '<h3>'.t("versioncontrol"). '</h3>' . $sRepoBar . '<br> + $renderAdminLTE->getCard( + [ + 'type' => '', + 'variant' => 'outline', + 'title' => '', + 'text' => '<h3>' . t("versioncontrol") . '</h3>' . $sRepoBar . '<br> ' . t("repositoryinfos") . '<br> ' // . $this->_aPrjConfig["build"]["type"] - . preg_replace('/.*\@(.*):.*/', '$1', $this->_aPrjConfig["build"]["url"]) - . '<br>(<strong title="' . t('branch-select') . '">' . count($aBranches) . '</strong>)', + . preg_replace('/.*\@(.*):.*/', '$1', $this->_aPrjConfig["build"]["url"]) + . '<br>(<strong title="' . t('branch-select') . '">' . count($aBranches) . '</strong>)', ] - ), 2 - ) + ), + 2 + ) . $renderAdminLTE->addCol( '<div class="action">' . $sContinue . t("build-hint-overview") . '<br><br>' . ($this->canAcceptPhase() ? $this->renderLink("build") : '') . '</div>', 1 ) . $renderAdminLTE->addCol( - $renderAdminLTE->getCard([ - 'type'=>'', - 'variant'=>'outline', - 'title'=>'', - 'text'=> '<h3>'.$this->_oHtml->getIcon('package') . t("archive"). '</h3>' - . '<div class="archive">' + $renderAdminLTE->getCard( + [ + 'type' => '', + 'variant' => 'outline', + 'title' => '', + 'text' => '<h3>' . $this->_oHtml->getIcon('package') . t("archive") . '</h3>' + . '<div class="archive">' . '<div class="details">' - . $sPackagebar . '<br>' + . $sPackagebar . '<br>' . '</div>' . t("packages") . '<br>' - .'(<strong>' . count($this->_getVersionUsage()) . '</strong>)' - . '</div>' + . '(<strong>' . count($this->_getVersionUsage()) . '</strong>)' + . '</div>' ] - ), 2 - ) + ), + 2 + ) . $renderAdminLTE->addCol( - '<div class="action">'.$sContinue . sprintf(t("queue-hint-overview"), $this->getNextPhase()).'</div>', + '<div class="action">' . $sContinue . sprintf(t("queue-hint-overview"), $this->getNextPhase()) . '</div>', 1 ) . $renderAdminLTE->addCol( - $renderAdminLTE->getCard([ - 'type'=>'', - 'variant'=>'outline', - 'title'=>'', - 'text'=> '<h3>'.$this->_oHtml->getIcon('phase') . t("phases"). '</h3>' - . ($sPhaseImg ? $sPhaseImg : '<div class="process">' . t("none") . '</div>') + $renderAdminLTE->getCard( + [ + 'type' => '', + 'variant' => 'outline', + 'title' => '', + 'text' => '<h3>' . $this->_oHtml->getIcon('phase') . t("phases") . '</h3>' + . ($sPhaseImg ? $sPhaseImg : '<div class="process">' . t("none") . '</div>') ] - ), 6 - ) + ), + 6 ) - .'</div>' - ; + ) + . '</div>' + ; return $sReturn; } diff --git a/public_html/deployment/classes/projectlist.class.php b/public_html/deployment/classes/projectlist.class.php index 155575b017c0453a8398761707072ed9ee3bca29..577c98ffc9d6fd2d280887bc1c576f1483e9652a 100644 --- a/public_html/deployment/classes/projectlist.class.php +++ b/public_html/deployment/classes/projectlist.class.php @@ -7,7 +7,9 @@ class projectlist to render overview page --------------------------------------------------------------------- - 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + Axel <axel.hahn@unibe.ch> + 2013-11-08 Axel + 2024-08-29 Axel php8 only; added variable types; short array syntax ###################################################################### */ require_once 'base.class.php'; @@ -50,7 +52,7 @@ class projectlist extends base * render html for overview table * @return string */ - public function renderOverview() + public function renderOverview(): string { global $renderAdminLTE; @@ -91,7 +93,7 @@ class projectlist extends base $oPrj = new projectgui(); $aProjectByLabel = $oPrj->getProjects("label"); foreach ($aProjectByLabel as $aProject) { - $sPrj=$aProject['id']; + $sPrj = $aProject['id']; $oPrj = new projectgui($sPrj); $sPrjFilter .= '<option value="' . $sPrj . '">' . $aProject['label'] . '</option>'; @@ -147,32 +149,32 @@ class projectlist extends base // render output $sOut .= ' <tr class="' . $sClasses . '" ' - . 'ondblclick="location.href=\'/deployment/' . $sPrj . '/\'" ' - . 'title="' . sprintf(t("overview-hint-dblclick"), $sPrj) + . 'ondblclick="location.href=\'/deployment/' . $sPrj . '/\'" ' + . 'title="' . sprintf(t("overview-hint-dblclick"), $sPrj) . '"> <td class="prj"> <span class="float-right"><i class="fa-solid fa-tag"></i> ' . $sPrjGroup . '</span> <strong>' - . $oHtml->getLink(array( + . $oHtml->getLink([ 'href' => '/deployment/' . $sPrj . '/', // 'title' => $oPrj->getDescription(), 'icon' => 'project', 'label' => $oPrj->getLabel() - )) + ]) . '</strong>' . '<div class="descr">' . $oPrj->getDescription() . '</div>' . ($sProgress ? '<div class="deployprogress">' . $sProgress . '</div>' : '') // . ' <br>' . '</td>' . '<td class="prj">' - . $oHtml->getLinkButton(array( + . $oHtml->getLinkButton([ 'href' => '#', 'onclick' => 'setProjectFilter(\'' . $sPrj . '\'); return false;', 'style' => 'float: right', 'title' => t("overview-filter-hint"), 'icon' => 'filter', 'label' => t("overview-filter"), - )) + ]) . '</td>' . '<td class="prj">'; if ($oPrj->canAcceptPhase()) { @@ -208,14 +210,7 @@ class projectlist extends base <div id="counter">...</div><br> <div class="filterbar"> - <form class="form-inline"> - - <!-- - <a href="#" class="view viewextended" onclick="setview(\'simple\');"><i class="glyphicon glyphicon-th-large"></i> ' . t("overview-simpleview") . '</a> - <a href="#" class="view viewsimple" onclick="setview(\'extended\');" ><i class="glyphicon glyphicon-th-list"></i> ' . t("overview-extview") . '</a> - | - --> - + <form class="form-inline"> <label for="efilter"> ' . $oHtml->getIcon('filter') . ' @@ -321,7 +316,7 @@ class projectlist extends base </thead> <tbody> ' . $sOut . '</tbody></table>' - ; + ; } else { $sOut = t("class-pl-error-no-project") . '<br><br>' . $oPrj1->renderLink("new"); diff --git a/public_html/deployment/classes/queryparam.class.php b/public_html/deployment/classes/queryparam.class.php index f4dd62ceabc92f29da08ebdb1a16759fc6d9bcbf..fd6b4448e9da5a6bd8da259672c8c732c154605e 100644 --- a/public_html/deployment/classes/queryparam.class.php +++ b/public_html/deployment/classes/queryparam.class.php @@ -1,35 +1,53 @@ <?php +/** + * Wrapping class to access $_GET|$_POST|$_SESSION variables with validation + * of a value + * + * @example + * Ensure that https://example.com/?page=<VALUE> will be accepted only if + * <VALUE> matches /^[a-z]*$/ + * <code>queryparam::get('page', '/^[a-z]*$/');</code> + * + * Ideas: + * - add more type validation + * - add regex param with chars to remove eg /[^a-z]/ to keep only lowercase letters + * + * Axel <axel.hahn@unibe.ch> + * 2024-08-29 Axel php8 only; added variable types; short array syntax + */ -class queryparam{ - +class queryparam +{ /** - * return value of a a given scope variable (e.g. $_GET|$_POST|$_SESSION) if it exists. + * Get value of a a given scope variable (e.g. $_GET|$_POST|$_SESSION) if it exists. * It will return NULLL if the value does not match an optional regex or type. * + * @param array $aScope array of scope variables * @param string $sVarname name of post or get variable (POST has priority) * @param string $sRegexMatch set a regex that must match * @param string $sType force type: false|int * @return mixed NULL|value */ - static public function getvar($aScope, $sVarname, $sRegexMatch=false, $sType=false){ + static public function getvar(array $aScope, string $sVarname, string $sRegexMatch = '', string $sType = ''): mixed + { // check if it exist - if(!isset($aScope[$sVarname])){ + if (!isset($aScope[$sVarname])) { return NULL; } - + $return = $aScope[$sVarname]; - + // verify regex - if ($sRegexMatch && !preg_match($sRegexMatch,$return)){ + if ($sRegexMatch && !preg_match($sRegexMatch, $return)) { return NULL; } - + // force given type - switch ($sType){ - case 'int': - $return=(int)$return; + switch ($sType) { + case 'int': + $return = (int) $return; break; } return $return; @@ -44,7 +62,8 @@ class queryparam{ * @param string $sType force type: false|int * @return mixed NULL|value */ - static function get($sVarname, $sRegexMatch=false, $sType=false) { + static function get(string $sVarname, string $sRegexMatch = '', string $sType = ''): mixed + { return self::getvar($_GET, $sVarname, $sRegexMatch, $sType); } @@ -56,23 +75,21 @@ class queryparam{ * @param string $sType force type: false|int * @return mixed NULL|value */ - static function getorpost($sVarname, $sRegexMatch=false, $sType=false) { + static function getorpost(string $sVarname, string $sRegexMatch = '', string $sType = ''): mixed + { // $this->logAdd(__METHOD__."($sVarname, $sRegexMatch, $sType) start"); - + // check if it exist - if(!isset($_POST[$sVarname]) && !isset($_GET[$sVarname])){ + if (!isset($_POST[$sVarname]) && !isset($_GET[$sVarname])) { // $this->logAdd(__METHOD__."($sVarname) $sVarname does not exist"); return false; } - + // set it to POST or GET variable - $aScope = isset($_POST[$sVarname]) && $_POST[$sVarname] - ? $_POST[$sVarname] - : ((isset($_GET[$sVarname]) && $_GET[$sVarname]) - ? $_GET[$sVarname] - : false - ) - ; + $aScope = isset($_POST[$sVarname]) + ? $_POST + : $_GET + ; return self::getvar($aScope, $sVarname, $sRegexMatch, $sType); } @@ -84,7 +101,8 @@ class queryparam{ * @param string $sType force type: false|int * @return mixed NULL|value */ - static function post($sVarname, $sRegexMatch=false, $sType=false) { + static function post(string $sVarname, string $sRegexMatch = '', string $sType = ''): mixed + { return self::getvar($_POST, $sVarname, $sRegexMatch, $sType); } diff --git a/public_html/deployment/classes/render-adminlte.class.php b/public_html/deployment/classes/render-adminlte.class.php index 5ce190406a400514ec4e34ae8ca3f40233346ce5..83cf626c624afc23bc6237422c52be5c2b01d7b4 100755 --- a/public_html/deployment/classes/render-adminlte.class.php +++ b/public_html/deployment/classes/render-adminlte.class.php @@ -1,155 +1,171 @@ <?php require_once 'htmlelements.class.php'; /** - * ====================================================================== + * ______________________________________________________________________ + * + * _ __ __ _ + * | || \/ || |__ Institute for Medical Education + * |_||_|\/|_||____| University of Bern + * + * ______________________________________________________________________ * * RENDERER FOR ADNINLTE template https://adminlte.io - * DOCS: https://adminlte.io/docs/3.2/ - * https://adminlte.io/themes/v3/index3.html + * its docs: https://adminlte.io/docs/3.2/ + * https://adminlte.io/themes/v3/index3.html * + * This is a php class to render + * - grid layout + * - navigation + * - widgets, components and forms + * + * DOCS: https://os-docs.iml.unibe.ch/adminlte-renderer/ * ---------------------------------------------------------------------- * 2023-09-11 <axel.hahn@unibe.ch> add shadows on card + callout * 2023-09-27 <axel.hahn@unibe.ch> add form input fields * 2023-11-17 <axel.hahn@unibe.ch> add tabbed content; "=" renders hamburger item + * 2024-05-03 <axel.hahn@unibe.ch> add line in sidebar menu; add getFormSelect + * 2024-05-10 <axel.hahn@unibe.ch> add support for bootstrap-select in getFormSelect + * 2024-05-18 <axel.hahn@unibe.ch> add variable types + * 2024-07-04 <axel.hahn@unibe.ch> added type declarations * ====================================================================== - * - * @author Axel */ -class renderadminlte { +class renderadminlte +{ - var $aPresets=[ + protected array $aPresets = [ - 'bgcolor'=>[ - 'description'=>'background colors', - 'group'=>'styling', - 'values'=>[ + 'bgcolor' => [ + 'description' => 'background colors', + 'group' => 'styling', + 'values' => [ // https://adminlte.io/themes/v3/pages/UI/general.html - ''=>'no value', - 'indigo'=>'indigo', - 'lightblue'=>'', - 'navy'=>'', - 'purple'=>'', - 'fuchsia'=>'', - 'pink'=>'', - 'maroon'=>'', - 'orange'=>'', - 'lime'=>'', - 'teal'=>'', - 'olive'=>'', - - 'black'=>'black', - 'dark'=>'dark gray', - 'gray'=>'gray', - 'light'=>'light gray', + '' => 'no value', + 'indigo' => 'indigo', + 'lightblue' => '', + 'navy' => '', + 'purple' => '', + 'fuchsia' => '', + 'pink' => '', + 'maroon' => '', + 'orange' => '', + 'lime' => '', + 'teal' => '', + 'olive' => '', + + 'black' => 'black', + 'dark' => 'dark gray', + 'gray' => 'gray', + 'light' => 'light gray', ] ], - - 'type'=>[ - 'description'=>'type or status like info/ warning/ danger to define a color', - 'group'=>'styling', - 'values'=>[ - ''=>'no value', - 'danger'=>'red', - 'info'=>'aqua', - 'primary'=>'blue', - 'secondary'=>'gray', - 'success'=>'green', - 'warning'=>'yellow', - 'dark'=>'dark gray', - 'gray'=>'gray', + + 'type' => [ + 'description' => 'type or status like info/ warning/ danger to define a color', + 'group' => 'styling', + 'values' => [ + '' => 'no value', + 'danger' => 'red', + 'info' => 'aqua', + 'primary' => 'blue', + 'secondary' => 'gray', + 'success' => 'green', + 'warning' => 'yellow', + 'dark' => 'dark gray', + 'gray' => 'gray', ] ], - 'shadow'=>[ - 'description'=>'use a shadow', - 'group'=>'styling', - 'values'=>[ - ''=>'no value', - 'none'=>'none', - 'small'=>'small', - 'regular'=>'regular', - 'large'=>'large' + 'shadow' => [ + 'description' => 'use a shadow', + 'group' => 'styling', + 'values' => [ + '' => 'no value', + 'none' => 'none', + 'small' => 'small', + 'regular' => 'regular', + 'large' => 'large' ] ], - 'size'=>[ - 'description'=>'set a size', - 'group'=>'styling', - 'values'=>[ - ''=>'no value', - 'lg'=>'', - 'sm'=>'', - 'xs'=>'', - 'flat'=>'', + 'size' => [ + 'description' => 'set a size', + 'group' => 'styling', + 'values' => [ + '' => 'no value', + 'lg' => '', + 'sm' => '', + 'xs' => '', + 'flat' => '', ] ], - 'variant'=>[ - 'description'=>'coloring style', - 'group'=>'styling', - 'values'=>[ - ''=>'no value', - 'outline'=>'small stripe on top', - 'solid'=>'full filled widget', - 'gradient'=>'full filled with gradient', + 'variant' => [ + 'description' => 'coloring style', + 'group' => 'styling', + 'values' => [ + '' => 'no value', + 'outline' => 'small stripe on top', + 'solid' => 'full filled widget', + 'gradient' => 'full filled with gradient', ] ], - 'visibility'=>[ - 'description'=>'', - 'group'=>'customizing', - 'values'=>[ - ''=>'no value', - '0'=>'hide', - '1'=>'show', + 'visibility' => [ + 'description' => '', + 'group' => 'customizing', + 'values' => [ + '' => 'no value', + '0' => 'hide', + '1' => 'show', ] ], // for keys: state - 'windowstate'=>[ - 'description'=>'state of a resizable widget', - 'group'=>'customizing', - 'values'=>[ - ''=>'no value', - 'collapsed'=>'header only', - 'maximized'=>'full window', + 'windowstate' => [ + 'description' => 'state of a resizable widget', + 'group' => 'customizing', + 'values' => [ + '' => 'no value', + 'collapsed' => 'header only', + 'maximized' => 'full window', ] ], // for keys: dismissable - 'yesno'=>[ - 'description'=>'', - 'group'=>'customizing', - 'values'=>[ - ''=>'no value', - '0'=>'no', - '1'=>'yes', + 'yesno' => [ + 'description' => '', + 'group' => 'customizing', + 'values' => [ + '' => 'no value', + '0' => 'no', + '1' => 'yes', ] ], ]; - var $_aValueMappings=[ - 'shadow'=>[ - 'default' => '', - 'none' => 'shadow-none', - 'small' => 'shadow-small', - 'regular' => 'shadow', - 'large' => 'shadow-lg', + protected array $_aValueMappings = [ + 'shadow' => [ + 'default' => '', + 'none' => 'shadow-none', + 'small' => 'shadow-small', + 'regular' => 'shadow', + 'large' => 'shadow-lg', ] ]; - var $_aElements=[]; - + protected array $_aElements = []; + /** - * instance of htmlelements + * instance of htmlelements object * @var object */ - var $_oHtml=false; - - + protected object $_oHtml; + + // ---------------------------------------------------------------------- // // CONSTRUCTOR // // ---------------------------------------------------------------------- - public function __construct() { - $this->_oHtml=new htmlelements(); + public function __construct() + { + $this->_oHtml = new htmlelements(); $this->_initElements(); - return true; + // return true; } // ---------------------------------------------------------------------- @@ -157,343 +173,380 @@ class renderadminlte { // PRIVATE FUNCTIONS // // ---------------------------------------------------------------------- - + /** * used in cosntructor * initialize all element definitions + * @return void */ - protected function _initElements(){ - $this->_aElements=[ + protected function _initElements(): void + { + $this->_aElements = [ // ------------------------------------------------------------ - 'alert'=>[ - 'label'=>'Alert', - 'description'=>'Colored box with title and a text', - 'method'=>'getAlert', - - 'params'=>[ - 'type' => ['select'=>$this->aPresets['type'], 'example_value'=>'warning'], - 'dismissible' => ['select'=>$this->aPresets['yesno'], 'example_value'=>''], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'' + 'alert' => [ + 'label' => 'Alert', + 'description' => 'Colored box with title and a text', + 'method' => 'getAlert', + + 'params' => [ + 'type' => ['select' => $this->aPresets['type'], 'example_value' => 'warning'], + 'dismissible' => ['select' => $this->aPresets['yesno'], 'example_value' => ''], + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => '' ], - 'title' => [ - 'description'=>'Title in a bit bigger font', - 'group'=>'content', - 'example_value'=>'Alert title' + 'title' => [ + 'description' => 'Title in a bit bigger font', + 'group' => 'content', + 'example_value' => 'Alert title' ], - 'text' => [ - 'description'=>'Message text', - 'group'=>'content', - 'example_value'=>'I am a message. Read me, please.' + 'text' => [ + 'description' => 'Message text', + 'group' => 'content', + 'example_value' => 'I am a message. Read me, please.' ], ] ], // ------------------------------------------------------------ - 'badge'=>[ - 'label'=>'Badge', - 'description'=>'Tiny additional info; mostly as counter', - 'method'=>'getBadge', - - 'params'=>[ - 'type' => ['select'=>$this->aPresets['type'], 'example_value'=>'danger'], - 'bgcolor' => ['select'=>$this->aPresets['bgcolor'], 'example_value'=>''], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'' + 'badge' => [ + 'label' => 'Badge', + 'description' => 'Tiny additional info; mostly as counter', + 'method' => 'getBadge', + + 'params' => [ + 'type' => ['select' => $this->aPresets['type'], 'example_value' => 'danger'], + 'bgcolor' => ['select' => $this->aPresets['bgcolor'], 'example_value' => ''], + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => '' ], - 'id' => [ - 'group'=>'customizing', - 'description'=>'optional: id attribute', - 'example_value'=>'' + 'id' => [ + 'group' => 'customizing', + 'description' => 'optional: id attribute', + 'example_value' => '' ], - 'title' => [ - 'group'=>'content', - 'description'=>'optional: title attribute for mouseover', - 'example_value'=>'Errors: 5' + 'title' => [ + 'group' => 'content', + 'description' => 'optional: title attribute for mouseover', + 'example_value' => 'Errors: 5' ], - 'text' => [ - 'group'=>'content', - 'description'=>'Text or value in the badge', - 'example_value'=>'5' + 'text' => [ + 'group' => 'content', + 'description' => 'Text or value in the badge', + 'example_value' => '5' ], ] ], // ------------------------------------------------------------ - 'button'=>[ - 'label'=>'Button', - 'description'=>'Buttons<br>In this component you can add other parmeter keys too - these will be added as attributes in the button tag.', - 'method'=>'getButton', - - 'params'=>[ - 'type' => ['select'=>$this->aPresets['type'], 'example_value'=>'primary'], - 'size' => ['select'=>$this->aPresets['size'], 'example_value'=>''], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'' + 'button' => [ + 'label' => 'Button', + 'description' => 'Buttons<br>In this component you can add other parmeter keys too - these will be added as attributes in the button tag.', + 'method' => 'getButton', + + 'params' => [ + 'type' => ['select' => $this->aPresets['type'], 'example_value' => 'primary'], + 'size' => ['select' => $this->aPresets['size'], 'example_value' => ''], + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => '' ], - 'text' => [ - 'group'=>'content', - 'description'=>'Text/ html code on the button', - 'example_value'=>'Click me' + 'text' => [ + 'group' => 'content', + 'description' => 'Text/ html code on the button', + 'example_value' => 'Click me' ], ] ], // ------------------------------------------------------------ - 'callout'=>[ - 'label'=>'Callout', - 'description'=>'Kind of infobox', - 'method'=>'getCallout', - - 'params'=>[ - 'type' => ['select'=>$this->aPresets['type'], 'example_value'=>'danger'], - 'shadow' => ['select'=>$this->aPresets['shadow'], 'example_value'=>''], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'' + 'callout' => [ + 'label' => 'Callout', + 'description' => 'Kind of infobox', + 'method' => 'getCallout', + + 'params' => [ + 'type' => ['select' => $this->aPresets['type'], 'example_value' => 'danger'], + 'shadow' => ['select' => $this->aPresets['shadow'], 'example_value' => ''], + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => '' ], - 'title' => [ - 'group'=>'content', - 'description'=>'Title in a bit bigger font', - 'example_value'=>'I am a callout' + 'title' => [ + 'group' => 'content', + 'description' => 'Title in a bit bigger font', + 'example_value' => 'I am a callout' ], - 'text' => [ - 'group'=>'content', - 'description'=>'Message text', - 'example_value'=>'Here is some description to whatever.' + 'text' => [ + 'group' => 'content', + 'description' => 'Message text', + 'example_value' => 'Here is some description to whatever.' ], ] ], // ------------------------------------------------------------ - 'card'=>[ - 'label'=>'Card', - 'description'=>'Content box with header, text, footer', - 'method'=>'getCard', - - 'params'=>[ - 'type' => ['select'=>$this->aPresets['type'], 'example_value'=>'primary'], - 'variant' => ['select'=>$this->aPresets['variant'], 'example_value'=>'outline'], - 'shadow' => ['select'=>$this->aPresets['shadow'], 'example_value'=>''], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'' + 'card' => [ + 'label' => 'Card', + 'description' => 'Content box with header, text, footer', + 'method' => 'getCard', + + 'params' => [ + 'type' => ['select' => $this->aPresets['type'], 'example_value' => 'primary'], + 'variant' => ['select' => $this->aPresets['variant'], 'example_value' => 'outline'], + 'shadow' => ['select' => $this->aPresets['shadow'], 'example_value' => ''], + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => '' ], - 'state' => [ - 'group'=>'customizing', - 'select'=>$this->aPresets['windowstate'], - 'example_value'=>'' + 'state' => [ + 'group' => 'customizing', + 'select' => $this->aPresets['windowstate'], + 'example_value' => '' ], - - 'tb-collapse' => ['description'=>'show minus symbol as collapse button', 'select'=>$this->aPresets['visibility'], 'example_value'=>''], - 'tb-expand' => ['description'=>'show plus symbol to expand card', 'select'=>$this->aPresets['visibility'], 'example_value'=>''], - 'tb-maximize' => ['description'=>'show maximize button for fullscreen', 'select'=>$this->aPresets['visibility'], 'example_value'=>''], - 'tb-minimize' => ['description'=>'show minimize button to minimize', 'select'=>$this->aPresets['visibility'], 'example_value'=>''], - 'tb-remove' => ['description'=>'show cross symbol to remove card', 'select'=>$this->aPresets['visibility'], 'example_value'=>''], - - 'title' => [ - 'group'=>'content', - 'description'=>'Title in the top row', - 'example_value'=>'I am a card' + + 'tb-collapse' => ['description' => 'show minus symbol as collapse button', 'select' => $this->aPresets['visibility'], 'example_value' => ''], + 'tb-expand' => ['description' => 'show plus symbol to expand card', 'select' => $this->aPresets['visibility'], 'example_value' => ''], + 'tb-maximize' => ['description' => 'show maximize button for fullscreen', 'select' => $this->aPresets['visibility'], 'example_value' => ''], + 'tb-minimize' => ['description' => 'show minimize button to minimize', 'select' => $this->aPresets['visibility'], 'example_value' => ''], + 'tb-remove' => ['description' => 'show cross symbol to remove card', 'select' => $this->aPresets['visibility'], 'example_value' => ''], + + 'title' => [ + 'group' => 'content', + 'description' => 'Title in the top row', + 'example_value' => 'I am a card' ], - 'tools' => [ - 'group'=>'content', - 'description'=>'Html code for the top right', - 'example_value'=>'' + 'tools' => [ + 'group' => 'content', + 'description' => 'Html code for the top right', + 'example_value' => '' ], - 'text' => [ - 'group'=>'content', - 'description'=>'Main content', - 'example_value'=>'Here is some beautiful content.' + 'text' => [ + 'group' => 'content', + 'description' => 'Main content', + 'example_value' => 'Here is some beautiful content.' ], - 'footer' => [ - 'group'=>'content', - 'description'=>'optional: footer content', - 'example_value'=>'Footer' + 'footer' => [ + 'group' => 'content', + 'description' => 'optional: footer content', + 'example_value' => 'Footer' ], ] ], // ------------------------------------------------------------ - 'infobox'=>[ - 'label'=>'Info box', - 'description'=>'Box with icon to highlight a single value; optional with a progress bar', - 'method'=>'getInfobox', - - 'params'=>[ - 'type'=>['select'=>$this->aPresets['type'], 'example_value'=>''], - 'iconbg'=>['select'=>$this->aPresets['type'], 'example_value'=>'info'], - 'shadow'=>['select'=>$this->aPresets['shadow'], 'example_value'=>''], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'' + 'infobox' => [ + 'label' => 'Info box', + 'description' => 'Box with icon to highlight a single value; optional with a progress bar', + 'method' => 'getInfobox', + + 'params' => [ + 'type' => ['select' => $this->aPresets['type'], 'example_value' => ''], + 'iconbg' => ['select' => $this->aPresets['type'], 'example_value' => 'info'], + 'shadow' => ['select' => $this->aPresets['shadow'], 'example_value' => ''], + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => '' ], - 'icon'=>[ - 'group'=>'content', - 'description'=>'css class for an icon', - 'example_value'=>'fa-regular fa-thumbs-up' + 'icon' => [ + 'group' => 'content', + 'description' => 'css class for an icon', + 'example_value' => 'fa-regular fa-thumbs-up' ], - 'text'=>[ - 'group'=>'content', - 'description'=>'short information text', - 'example_value'=>'Likes' + 'text' => [ + 'group' => 'content', + 'description' => 'short information text', + 'example_value' => 'Likes' ], - 'number'=>[ - 'group'=>'content', - 'description'=>'a number to highlight', - 'example_value'=>"41,410" + 'number' => [ + 'group' => 'content', + 'description' => 'a number to highlight', + 'example_value' => "41,410" ], - 'progressvalue'=>[ - 'group'=>'content', - 'description'=>'optional: progress value 0..100 to draw a progress bar', - 'example_value'=>70 + 'progressvalue' => [ + 'group' => 'content', + 'description' => 'optional: progress value 0..100 to draw a progress bar', + 'example_value' => 70 ], - 'progresstext'=>[ - 'group'=>'content', - 'description'=>'optional: text below progress bar', - 'example_value'=>'70% Increase in 30 Days' + 'progresstext' => [ + 'group' => 'content', + 'description' => 'optional: text below progress bar', + 'example_value' => '70% Increase in 30 Days' ] ] ], // ------------------------------------------------------------ - 'smallbox'=>[ - 'label'=>'Small box', - 'description'=>'Solid colored box to highlight a single value; optional with a link', - 'method'=>'getSmallbox', - - 'params'=>[ - 'type'=>['select'=>$this->aPresets['type'], 'example_value'=>'info'], - 'shadow'=>['select'=>$this->aPresets['shadow'], 'example_value'=>''], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'' + 'smallbox' => [ + 'label' => 'Small box', + 'description' => 'Solid colored box to highlight a single value; optional with a link', + 'method' => 'getSmallbox', + + 'params' => [ + 'type' => ['select' => $this->aPresets['type'], 'example_value' => 'info'], + 'shadow' => ['select' => $this->aPresets['shadow'], 'example_value' => ''], + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => '' ], - 'icon'=>['group'=>'content', 'description'=>'css class for an icon', 'example_value'=>'fa-solid fa-shopping-cart'], - - 'text'=>['group'=>'content', 'description'=>'short information text', 'example_value'=>'New orders'], - 'number'=>['group'=>'content', 'description'=>'a number to highlight', 'example_value'=>"150"], - 'url'=>['group'=>'content', 'description'=>'optional: url to set a link on the bottom', 'example_value'=>'#'], - 'linktext'=>['group'=>'content', 'description'=>'used if a url was given: linked text', 'example_value'=>'More info'] + 'icon' => ['group' => 'content', 'description' => 'css class for an icon', 'example_value' => 'fa-solid fa-shopping-cart'], + + 'text' => ['group' => 'content', 'description' => 'short information text', 'example_value' => 'New orders'], + 'number' => ['group' => 'content', 'description' => 'a number to highlight', 'example_value' => "150"], + 'url' => ['group' => 'content', 'description' => 'optional: url to set a link on the bottom', 'example_value' => '#'], + 'linktext' => ['group' => 'content', 'description' => 'used if a url was given: linked text', 'example_value' => 'More info'] ] ], // ------------------------------------------------------------ - 'input'=>[ - 'label'=>'Form: input', - 'description'=>'Input form fiels', - 'method'=>'getFormInput', - - 'params'=>[ - 'label' => [ - 'group'=>'styling', - 'description'=>'label for the input field', - 'example_value'=>'Enter something' + 'input' => [ + 'label' => 'Form: input', + 'description' => 'Input form fiels', + 'method' => 'getFormInput', + + 'params' => [ + 'label' => [ + 'group' => 'styling', + 'description' => 'label for the input field', + 'example_value' => 'Enter something' ], - 'type'=>['select'=> [ - 'description'=>'type or input field', - 'group'=>'styling', - 'values'=>[ - 'text'=>'text', - 'password'=>'password', - 'email'=>'email', - 'hidden'=>'hidden', - - 'button'=>'button', - 'checkbox'=>'checkbox', - 'color'=>'color', - 'date'=>'date', - 'datetime-local'=>'datetime-local', - 'email'=>'email', - 'file'=>'file', - 'hidden'=>'hidden', - 'image'=>'image', - 'month'=>'month', - 'number'=>'number', - 'password'=>'password', - 'radio'=>'radio', - 'range'=>'range', - 'reset'=>'reset', - 'search'=>'search', - 'submit'=>'submit', - 'tel'=>'tel', - 'text'=>'text', - 'time'=>'time', - 'url'=>'url', - 'week'=>'week', - ] - ], - 'example_value'=>'text' + 'type' => [ + 'select' => [ + 'description' => 'type or input field', + 'group' => 'styling', + 'values' => [ + + 'button' => 'button', + 'checkbox' => 'checkbox', + 'color' => 'color', + 'date' => 'date', + 'datetime-local' => 'datetime-local', + 'email' => 'email', + 'file' => 'file', + 'hidden' => 'hidden', + 'image' => 'image', + 'month' => 'month', + 'number' => 'number', + 'password' => 'password', + 'radio' => 'radio', + 'range' => 'range', + 'reset' => 'reset', + 'search' => 'search', + 'submit' => 'submit', + 'tel' => 'tel', + 'text' => 'text', + 'time' => 'time', + 'url' => 'url', + 'week' => 'week', + ] + ], + 'example_value' => 'text' ], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'myclass' + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => 'myclass' ], - 'prepend' => [ - 'group'=>'styling', - 'description'=>'optional: content on input start', - 'example_value'=>'' + 'prepend' => [ + 'group' => 'styling', + 'description' => 'optional: content on input start', + 'example_value' => '' ], - 'append' => [ - 'group'=>'styling', - 'description'=>'optional: content on input end', - 'example_value'=>'' + 'append' => [ + 'group' => 'styling', + 'description' => 'optional: content on input end', + 'example_value' => '' ], - 'name' => [ - 'group'=>'content', - 'description'=>'name attribute', - 'example_value'=>'firstname' + 'name' => [ + 'group' => 'content', + 'description' => 'name attribute', + 'example_value' => 'firstname' ], 'value' => [ - 'group'=>'content', - 'description'=>'Value', - 'example_value'=>'Jack' + 'group' => 'content', + 'description' => 'Value', + 'example_value' => 'Jack' ], ] ], // ------------------------------------------------------------ - 'textarea'=>[ - 'label'=>'Form: textarea', - 'description'=>'textarea or html editor', - 'method'=>'getFormTextarea', - - 'params'=>[ - 'label' => [ - 'group'=>'styling', - 'description'=>'label for the input field', - 'example_value'=>'Enter text' + // WIP + 'select' => [ + 'label' => 'Form: select', + 'description' => 'Select box', + 'method' => 'getFormSelect', + + 'params' => [ + 'label' => [ + 'group' => 'styling', + 'description' => 'label for the select field', + 'example_value' => 'Enter text' + ], + 'bootstrap-select' => [ + 'select' => [ + 'description' => 'Enable bootstrap-select plugin', + 'group' => 'styling', + 'values' => [ + '0' => 'no', + '1' => 'yes', + ] + ] + ], + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => 'myclass' + ], + 'options' => [ + 'example_value' => [ + ["value" => "1", "label" => "one"], + ["value" => "2", "label" => "two"], + ["value" => "3", "label" => "three"], + ], + ] + ], + ], + // ------------------------------------------------------------ + 'textarea' => [ + 'label' => 'Form: textarea', + 'description' => 'textarea or html editor', + 'method' => 'getFormTextarea', + + 'params' => [ + 'label' => [ + 'group' => 'styling', + 'description' => 'label for the input field', + 'example_value' => 'Enter text' ], - 'type'=>['select'=> [ - 'description'=>'type or input field', - 'group'=>'styling', - 'values'=>[ - ''=>'text', - 'html'=>'html editor', - ] - ], + 'type' => [ + 'select' => [ + 'description' => 'type or input field', + 'group' => 'styling', + 'values' => [ + '' => 'text', + 'html' => 'html editor', + ] + ], ], - 'class' => [ - 'group'=>'styling', - 'description'=>'optional: css classes', - 'example_value'=>'myclass' + 'class' => [ + 'group' => 'styling', + 'description' => 'optional: css classes', + 'example_value' => 'myclass' ], - 'name' => [ - 'group'=>'content', - 'description'=>'name attribute', - 'example_value'=>'textdata' + 'name' => [ + 'group' => 'content', + 'description' => 'name attribute', + 'example_value' => 'textdata' ], 'value' => [ - 'group'=>'content', - 'description'=>'Value', - 'example_value'=>'Here is some text...' + 'group' => 'content', + 'description' => 'Value', + 'example_value' => 'Here is some text...' ], ] - ], + ], ]; } /** @@ -502,10 +555,12 @@ class renderadminlte { * @param array $aAttributes array of its attributes * @param string $sContent content between opening and closing tag * @param bool $bClosetag flag: write a closing tag or not? default: true + * @return string */ - protected function _tag($sTag, $aAttributes, $sContent=false, $bClosetag=true){ - if ($sContent){ - $aAttributes['label']=(isset($aAttributes['label']) ? $aAttributes['label'] : '') . $sContent; + protected function _tag(string $sTag, array $aAttributes, string $sContent = '', bool $bClosetag = true): string + { + if ($sContent) { + $aAttributes['label'] = (isset($aAttributes['label']) ? $aAttributes['label'] : '') . $sContent; } return $this->_oHtml->getTag($sTag, $aAttributes, $bClosetag); } @@ -519,25 +574,29 @@ class renderadminlte { * render a page by using template * @param string $stemplate html template with placeholders * @param array $aReplace key = what to replace .. value = new value + * @return string */ - public function render($sTemplate, $aReplace){ + public function render(string $sTemplate, array $aReplace): string + { return str_replace( array_keys($aReplace), array_values($aReplace), $sTemplate ); } - /** * add a wrapper: wrap some content into a tag * + * @param string $sTag name of html tag + * @param array $aOptions array of its attributes * @param string $sContent html content inside * @return string */ - public function addWrapper($sTag, $aOptions, $sContent){ - $aOptions['label']=$sContent; - return $this->_tag($sTag, $aOptions)."\n"; + public function addWrapper(string $sTag, array $aOptions, string $sContent): string + { + $aOptions['label'] = $sContent; + return $this->_tag($sTag, $aOptions) . PHP_EOL; } // ---------------------------------------------------------------------- @@ -546,7 +605,6 @@ class renderadminlte { // // ---------------------------------------------------------------------- - /** * get a single navigation item on top bar * @@ -554,80 +612,93 @@ class renderadminlte { * @param integer $iLevel Menu level; 1=top bar; 2=pupup menu with subitems * @return string */ - public function getNavItem($aLink, $iLevel=1){ + public function getNavItem(array $aLink, int $iLevel = 1): string + { static $iCounter; - if(!isset($iCounter)){ - $iCounter=0; + if (!isset($iCounter)) { + $iCounter = 0; } - + switch ($aLink['label']) { // special menu entry: horizontal bar (label is "-") - case '-': return '<div class="dropdown-divider"></div>'; break; + case '-': + return '<div class="dropdown-divider"></div>'; + break; // special menu entry: hamburger menu item (label is "=") - case '=': return '<li class="nav-item"><a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fa-solid fa-bars"></i></a></li>'; break; + case '=': + return '<li class="nav-item"><a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a></li>'; + break; // special menu entry: hamburger menu item (label is "|") // requires css: .navbar-nav li.divider{border-left: 1px solid rgba(0,0,0,0.2);} - case '|': return '<li class="divider"></li>'; break; + case '|': + return '<li class="divider"></li>'; + break; } - $aChildren=isset($aLink['children']) && is_array($aLink['children']) && count($aLink['children']) ? $aLink['children'] : false; + $aChildren = isset($aLink['children']) && is_array($aLink['children']) && count($aLink['children']) ? $aLink['children'] : false; - $aLink['class']='nav-link' - . (isset($aLink['class']) ? ' '.$aLink['class'] : '') + $aLink['class'] = 'nav-link' + . (isset($aLink['class']) ? ' ' . $aLink['class'] : '') . ($aChildren ? ' dropdown-toggle' : '') - ; - if($aChildren){ + ; + if ($aChildren) { $iCounter++; - $sNavId="navbarDropdown".$iCounter; - $aLink=array_merge($aLink,[ - 'id'=>$sNavId, - 'role'=>"button", - 'data-toggle'=>"dropdown", - 'aria-haspopup'=>"true", - 'aria-expanded'=>"false", + $sNavId = "navbarDropdown" . $iCounter; + $aLink = array_merge($aLink, [ + 'id' => $sNavId, + 'role' => "button", + 'data-toggle' => "dropdown", + 'aria-haspopup' => "true", + 'aria-expanded' => "false", ]); unset($aLink['children']); // remove from html attributes to draw } - $sReturn=$this->_tag('a', $aLink)."\n"; - if($aChildren){ + $sReturn = $this->_tag('a', $aLink) . "\n"; + if ($aChildren) { $iLevel++; - $sReturn.=$this->addWrapper( - 'div', - [ - 'aria-labelledby'=> $sNavId, - 'class'=>'dropdown-menu' - ], - $this->getNavItems($aChildren, $iLevel) - )."\n"; + $sReturn .= $this->addWrapper( + 'div', + [ + 'aria-labelledby' => $sNavId, + 'class' => 'dropdown-menu' + ], + $this->getNavItems($aChildren, $iLevel) + ) . "\n"; $iLevel--; } - - if($iLevel==1){ - $sLiClass='nav-item'.($aChildren ? ' dropdown' : ''); - $sReturn=$this->addWrapper( - 'li', - ['class'=>$sLiClass], - $sReturn + + if ($iLevel == 1) { + $sLiClass = 'nav-item' . ($aChildren ? ' dropdown' : ''); + $sReturn = $this->addWrapper( + 'li', + ['class' => $sLiClass], + $sReturn ); } return $sReturn; } + /** - * get a navigation item for top bar + * get html code for navigation on top bar * + * @param array $aLinks array of navigation items + * @param int $iLevel current navigation level; default: 1 + * @return string|bool */ - public function getNavItems($aLinks, $iLevel=1){ - $sReturn=''; - if (!$aLinks || !is_array($aLinks) || !count($aLinks)){ + public function getNavItems(array $aLinks, int $iLevel = 1): string|bool + { + $sReturn = ''; + if (!$aLinks || !is_array($aLinks) || !count($aLinks)) { return false; } - foreach($aLinks as $aLink){ - $sReturn.=$this->getNavItem($aLink, $iLevel); + foreach ($aLinks as $aLink) { + $sReturn .= $this->getNavItem($aLink, $iLevel); } return $sReturn; } + /** * get a top left navigation for a top navigation bar * @@ -649,69 +720,94 @@ class renderadminlte { * .'</nav>' * </code> * - * @param array $aNavitems array of navigation items/ tree - * @param array $aUlOptions array of html attrubutes for wrapping UL tag + * @param array $aNavItems array of navigation items/ tree + * @param array $aUlOptions array of html attrubutes for wrapping UL tag + * @param array $aNavItemsRight array of html attrubutes for wrapping UL tag + * @param array $aUlOptionsRight array of html attrubutes for wrapping UL tag * @return string */ - public function getTopNavigation($aNavItems, $aUlOptions=false, $aNavItemsRight=[], $aUlOptionsRight=false){ + public function getTopNavigation(array $aNavItems, array $aUlOptions = [], array $aNavItemsRight = [], array $aUlOptionsRight = []): string + { // array_unshift($aNavItems, ['class'=>'nav-link', 'data-widget'=>'pushmenu', 'href'=>'#', 'role'=>'button', 'label'=>'<i class="fa-solid fa-bars"></i>']); - $aUlOptLeft=$aUlOptions ? $aUlOptions : ['class'=>'navbar-nav']; - $aUlOptRight=$aUlOptionsRight ? $aUlOptionsRight : ['class'=>'navbar-nav ml-auto']; + $aUlOptLeft = count($aUlOptions) ? $aUlOptions : ['class' => 'navbar-nav']; + $aUlOptRight = count($aUlOptionsRight) ? $aUlOptionsRight : ['class' => 'navbar-nav ml-auto']; return $this->addWrapper('ul', $aUlOptLeft, $this->getNavItems($aNavItems)) - .(count($aNavItemsRight) + . (count($aNavItemsRight) ? $this->addWrapper('ul', $aUlOptRight, $this->getNavItems($aNavItemsRight)) : '' ) - ; + ; } // ---------------------------------------------------------------------- /** - * get a navigation items for sidebar + * Get a navigation items for sidebar + * Links can be nested with the key "children". + * + * Remark: for a horizontal line ($aLink['label']='-') this css is required + * .nav-item hr{color: #505860; border-top: 1px solid; height: 1px; padding: 0; margin: 0; } + * * @param array $aLinks list of link items + * @return string|bool */ - public function getNavi2Items($aLinks){ - $sReturn=''; - if (!$aLinks || !is_array($aLinks) || !count($aLinks)){ + public function getNavi2Items(array $aLinks): string|bool + { + $sReturn = ''; + if (!$aLinks || !is_array($aLinks) || !count($aLinks)) { return false; } - foreach($aLinks as $aLink){ + + foreach ($aLinks as $aLink) { + + if ($aLink['label'] == '-') { + // TODO: draw a nice line + $sReturn .= '<li class="nav-item"><hr></li>'; + continue; + } // to render active or open links: - $aLink['class']='nav-link' . (isset($aLink['class']) ? ' '.$aLink['class'] : ''); - - $aChildren=isset($aLink['children']) ? $aLink['children'] : false; - $aLiClass='nav-item' . ($aChildren && strstr($aLink['class'], 'active') ? ' menu-open' : ''); - $sSubmenu=''; - if($aChildren){ - unset($aLink['children']); - $aLink['label'].='<i class="right fa-solid fa-angle-left"></i>'; - $sSubmenu.=$this->getSidebarNavigation($aChildren, ['class'=>'nav nav-treeview']); + $aLink['class'] = 'nav-link' . (isset($aLink['class']) ? ' ' . $aLink['class'] : ''); + + $aChildren = isset($aLink['children']) ? $aLink['children'] : false; + $aLiClass = 'nav-item' . ($aChildren && strstr($aLink['class'], 'active') ? ' menu-open' : ''); + $sSubmenu = ''; + if ($aChildren) { + unset($aLink['children']); + $aLink['label'] .= '<i class="right fa-solid fa-angle-left"></i>'; + $sSubmenu .= $this->getSidebarNavigation($aChildren, ['class' => 'nav nav-treeview']); } - $aLink['label']=$this->addWrapper('p', [], $aLink['label']); - $sReturn.=$this->addWrapper( - 'li', ['class'=>$aLiClass], - $this->_tag('a', $aLink).$sSubmenu - )."\n"; + $aLink['label'] = $this->addWrapper('p', [], $aLink['label']); + $sReturn .= $this->addWrapper( + 'li', + ['class' => $aLiClass], + $this->_tag('a', $aLink) . $sSubmenu + ) . "\n"; } return $sReturn; } /** + * get html code for sidebar navigation * + * @param array $aNavItems navigation item + * @param array $aUlOptions aatributes for UL tag + * @param string */ - public function getSidebarNavigation($aNavItems, $aUlOptions=[ - 'class'=>'nav nav-pills nav-sidebar flex-column nav-flat_ nav-child-indent', - 'data-widget'=>'treeview', - 'role'=>'menu', - 'data-accordion'=>'false' - ]) - { + public function getSidebarNavigation( + array $aNavItems, + array $aUlOptions = [ + 'class' => 'nav nav-pills nav-sidebar flex-column nav-flat_ nav-child-indent', + 'data-widget' => 'treeview', + 'role' => 'menu', + 'data-accordion' => 'false' + ] + ): string { return $this->addWrapper( - 'ul', $aUlOptions, - $this->getNavi2Items($aNavItems) - )."\n"; + 'ul', + $aUlOptions, + $this->getNavi2Items($aNavItems) + ) . "\n"; } // ---------------------------------------------------------------------- @@ -719,15 +815,16 @@ class renderadminlte { // PUBLIC FUNCTIONS :: CONTENT - BASIC FUNCTIONS // // ---------------------------------------------------------------------- - + /** * add page row * * @param string $sContent html content inside * @return string */ - public function addRow($sContent){ - return $this->addWrapper('div', ['class'=>'row'], $sContent); + public function addRow(string $sContent): string + { + return $this->addWrapper('div', ['class' => 'row'], $sContent); } /** @@ -738,8 +835,9 @@ class renderadminlte { * @param string $sFloat css value for float attribute; default=false * @return string */ - public function addCol($sContent, $iCols=6, $sFloat=false){ - return $this->addWrapper('div', ['class'=>'col-md-'.$iCols, 'style'=>'float:'.$sFloat], $sContent); + public function addCol(string $sContent, int $iCols = 6, string $sFloat = ''): string + { + return $this->addWrapper('div', ['class' => 'col-md-' . $iCols, 'style' => 'float:' . $sFloat], $sContent); } // ---------------------------------------------------------------------- @@ -748,49 +846,52 @@ class renderadminlte { // // ---------------------------------------------------------------------- - /** * get a list of all defined components that can be rendered - * @param {bool} $bSendData flag: send including subkeys of the hash; default: false (keys only) - * @return {array} + * @param bool $bSendData flag: send including subkeys of the hash; default: false (keys only) + * @return array */ - public function getComponents($bSendData=false){ + public function getComponents(bool $bSendData = false): array + { return $bSendData ? $this->_aElements : array_keys($this->_aElements) ; } + /** * get data of a component - * @param {string} $sComponent id of the component - * @return {array} + * @param string $sComponent id of the component + * @return array|bool */ - public function getComponent($sComponent){ - if(!isset($this->_aElements[$sComponent])){ + public function getComponent(string $sComponent): array|bool + { + if (!isset($this->_aElements[$sComponent])) { return false; } - $aReturn=array_merge(['id'=>$sComponent], $this->_aElements[$sComponent]); + $aReturn = array_merge(['id' => $sComponent], $this->_aElements[$sComponent]); unset($aReturn['params']); return $aReturn; } /** * get parameter keys of a component - * @param {string} $sComponent id of the component - * @param {bool} $bSendData flag: send including subkeys of the hash; default: false (keys only) - * @return {array} + * @param string $sComponent id of the component + * @param bool $bSendData flag: send including subkeys of the hash; default: false (keys only) + * @return array|bool */ - public function getComponentParamkeys($sComponent, $bSendData=false){ - if(!isset($this->_aElements[$sComponent])){ + public function getComponentParamkeys(string $sComponent, bool $bSendData = false) + { + if (!isset($this->_aElements[$sComponent])) { return false; } - $aKeys=array_keys($this->_aElements[$sComponent]['params']); - if(!$bSendData){ + $aKeys = array_keys($this->_aElements[$sComponent]['params']); + if (!$bSendData) { return $aKeys; } - $aReturn=[]; - foreach($aKeys as $sKey){ - $aReturn[$sKey]=$this->getComponentParamkey($sComponent, $sKey); + $aReturn = []; + foreach ($aKeys as $sKey) { + $aReturn[$sKey] = $this->getComponentParamkey($sComponent, $sKey); } // $aReturn=$this->_aElements[$sComponent]['params']; return $aReturn; @@ -798,34 +899,36 @@ class renderadminlte { /** * get information a parameter keys of a component - * @param {string} $sComponent id of the component - * @param {string} $sKey key in the options array - * @return {array} + * @param string $sComponent id of the component + * @param string $sKey key in the options array + * @return array|bool */ - public function getComponentParamkey($sComponent, $sKey){ - if(!isset($this->_aElements[$sComponent]['params'][$sKey])){ + public function getComponentParamkey(string $sComponent, string $sKey): array|bool + { + if (!isset($this->_aElements[$sComponent]['params'][$sKey])) { return false; } - $aReturn=$this->_aElements[$sComponent]['params'][$sKey]; + $aReturn = $this->_aElements[$sComponent]['params'][$sKey]; // get description from a preset - if(!isset($aReturn['description']) && isset($aReturn['select']['description'])){ - $aReturn['description']=$aReturn['select']['description']; + if (!isset($aReturn['description']) && isset($aReturn['select']['description'])) { + $aReturn['description'] = $aReturn['select']['description']; } - if(!isset($aReturn['group']) && isset($aReturn['select']['group'])){ - $aReturn['group']=$aReturn['select']['group']; + if (!isset($aReturn['group']) && isset($aReturn['select']['group'])) { + $aReturn['group'] = $aReturn['select']['group']; } return $aReturn; } /** * get a flat list of valid parameters for a key in a component - * @param {string} $sComponent id of the component - * @param {string} $sKey key in the options array - * @return {array} + * @param string $sComponent id of the component + * @param string $sKey key in the options array + * @return array|bool */ - public function getValidParamValues($sComponent, $sKey){ - $aOptionkey=$this->getComponentParamkey($sComponent, $sKey); - if(!$aOptionkey || !isset($aOptionkey['select']['values'])){ + public function getValidParamValues(string $sComponent, string $sKey): array|bool + { + $aOptionkey = $this->getComponentParamkey($sComponent, $sKey); + if (!$aOptionkey || !isset($aOptionkey['select']['values'])) { return false; } return array_keys($aOptionkey['select']['values']); @@ -838,43 +941,49 @@ class renderadminlte { * helper: add a css value with prefix * this handles option keys in get[COMPONENT] methods * if a value is set then this function returns a space + prefix (param 2) + value - * @param {string} $sValue option value - * @param {string} $sPrefix prefix in front of css value - * @return {string} + * @param string $sValue option value + * @param string $sPrefix prefix in front of css value + * @return string */ - protected function _addClassValue($sValue, $sPrefix=''){ - return $sValue ? ' '.$sPrefix.$sValue : ''; + protected function _addClassValue(string $sValue, string $sPrefix = ''): string + { + return $sValue ? ' ' . $sPrefix . $sValue : ''; } /** * helper function for get[COMPONENTNAME] methods: - * ensure that all wanted keys exist in an array. Non existing keys will be added with value false + * ensure that all wanted keys exist in an array. Non existing keys will + * be added with value false + * + * @param string $sComponent id of the component + * @param array $aOptions hash with keys for all options * @return array */ - protected function _ensureOptions($sComponent, $aOptions=[]){ + protected function _ensureOptions(string $sComponent, array $aOptions = []): array + { - $aParams=$this->getComponentParamkeys($sComponent, 0); - if(!$aParams){ - $aOptions['_infos'][]="Warning: no definition was found for component $sComponent."; + $aParams = $this->getComponentParamkeys($sComponent, 0); + if (!$aParams) { + $aOptions['_infos'][] = "Warning: no definition was found for component $sComponent."; return $aOptions; } - foreach ($aParams as $sKey){ - if(!isset($aOptions) || !isset($aOptions[$sKey])){ - $aOptions[$sKey]=false; - if(!isset($aOptions['_infos'])){ - $aOptions['_infos']=[]; + foreach ($aParams as $sKey) { + if (!isset($aOptions) || !isset($aOptions[$sKey])) { + $aOptions[$sKey] = false; + if (!isset($aOptions['_infos'])) { + $aOptions['_infos'] = []; } - $aOptions['_infos'][]="added missing key: $sKey"; + $aOptions['_infos'][] = "added missing key: $sKey"; } // $aParamdata - $aValidvalues=$this->getValidParamValues($sComponent, $sKey); - if($aValidvalues){ - if(array_search($aOptions[$sKey], $aValidvalues)===false){ - echo "ERROR: [".$sComponent."] value "".$aOptions[$sKey]."" is not a valid for param key [".$sKey."]; it must be one of ".implode("|", $aValidvalues).'<br>'; + $aValidvalues = $this->getValidParamValues($sComponent, $sKey); + if ($aValidvalues) { + if (array_search($aOptions[$sKey], $aValidvalues) === false) { + echo "ERROR: [" . $sComponent . "] value "" . $aOptions[$sKey] . "" is not a valid for param key [" . $sKey . "]; it must be one of " . implode("|", $aValidvalues) . '<br>'; } } - + // $this->_checkValue($sKey, $aOptions[$sKey], __METHOD__); } // echo '<pre>' . print_r($aOptions, 1) . '</pre>'; @@ -890,35 +999,36 @@ class renderadminlte { * return a alert box * https://adminlte.io/themes/v3/pages/UI/general.html * - * @param type $aOptions hash with keys for all options + * @param array $aOptions hash with keys for all options * - type - one of [none]|danger|info|primary|success|warning * - dismissible - if dismissible - one of true|false; default: false * - title * - text * @return string */ - public function getAlert($aOptions){ - $aOptions=$this->_ensureOptions('alert', $aOptions); - $aAlertIcons=[ - 'danger'=>'icon fa-solid fa-ban', - 'info'=>'icon fa-solid fa-info', - 'warning'=>'icon fa-solid fa-exclamation-triangle', - 'success'=>'icon fa-solid fa-check', + public function getAlert(array $aOptions): string + { + $aOptions = $this->_ensureOptions('alert', $aOptions); + $aAlertIcons = [ + 'danger' => 'icon fa-solid fa-ban', + 'info' => 'icon fa-solid fa-info', + 'warning' => 'icon fa-solid fa-exclamation-triangle', + 'success' => 'icon fa-solid fa-check', ]; - $aElement=[ - 'class'=>'alert' + $aElement = [ + 'class' => 'alert' . $this->_addClassValue($aOptions['type'], 'alert-') . $this->_addClassValue($aOptions['dismissible'], 'alert-') - , - 'label'=>'' + , + 'label' => '' . ($aOptions['dismissible'] ? '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' : '') . $this->_tag('h5', [ - 'label'=> '' - .(isset($aAlertIcons[$aOptions['type']]) ? '<i class="'.$aAlertIcons[$aOptions['type']].'"></i> ' : '') - .$aOptions['title'] - ]) - .$aOptions['text'] + 'label' => '' + . (isset($aAlertIcons[$aOptions['type']]) ? '<i class="' . $aAlertIcons[$aOptions['type']] . '"></i> ' : '') + . $aOptions['title'] + ]) + . $aOptions['text'] ]; return $this->_tag('div', $aElement); @@ -940,20 +1050,22 @@ class renderadminlte { * - text - visible text * - title - optional: title attribute * - type - one of [none]|danger|dark|info|primary|secondary|success|warning + * @return string */ - public function getBadge($aOptions){ - $aOptions=$this->_ensureOptions('badge', $aOptions); - $aElement=[]; - $aElement['class']='badge' - . $this->_addClassValue($aOptions['class'], '') - . $this->_addClassValue($aOptions['type'], 'badge-') + public function getBadge(array $aOptions): string + { + $aOptions = $this->_ensureOptions('badge', $aOptions); + $aElement = []; + $aElement['class'] = 'badge' + . $this->_addClassValue($aOptions['class'], '') + . $this->_addClassValue($aOptions['type'], 'badge-') . $this->_addClassValue($aOptions['bgcolor'], 'bg-') - ; - if ($aOptions['id']){ - $aElement['id']=$aOptions['id']; + ; + if ($aOptions['id']) { + $aElement['id'] = $aOptions['id']; } - $aElement['title']=$aOptions['title']; - $aElement['label']=$aOptions['text']; + $aElement['title'] = $aOptions['title']; + $aElement['label'] = $aOptions['text']; return $this->_tag('span', $aElement); } @@ -965,7 +1077,7 @@ class renderadminlte { * https://adminlte.io/themes/v3/pages/UI/buttons.html * * <button type="button" class="btn btn-block btn-default">Default</button> - * @param type $aOptions hash with keys for all options + * @param array $aOptions hash with keys for all options * - type - one of [none]|danger|dark|info|primary|secondary|success|warning * - size - one of [none]|lg|sm|xs|flat * - class - any css class for customizing, eg. "disabled" @@ -973,16 +1085,17 @@ class renderadminlte { * - text - text on button * @return string */ - public function getButton($aOptions){ - $aOptions=$this->_ensureOptions('button', $aOptions); - $aElement=$aOptions; - $aElement['class']='btn' - . $this->_addClassValue($aOptions['type'], 'btn-') - . $this->_addClassValue($aOptions['size'], 'btn-') - . $this->_addClassValue($aOptions['class'], '') - ; - $aElement['label']=$aOptions['text'] ? $aOptions['text'] : ' '; - foreach(['_infos', 'type', 'size', 'icon', 'text'] as $sDeleteKey){ + public function getButton(array $aOptions): string + { + $aOptions = $this->_ensureOptions('button', $aOptions); + $aElement = $aOptions; + $aElement['class'] = 'btn' + . $this->_addClassValue($aOptions['type'], 'btn-') + . $this->_addClassValue($aOptions['size'], 'btn-') + . $this->_addClassValue($aOptions['class'], '') + ; + $aElement['label'] = $aOptions['text'] ? $aOptions['text'] : ' '; + foreach (['_infos', 'type', 'size', 'icon', 'text'] as $sDeleteKey) { unset($aElement[$sDeleteKey]); } return $this->_tag('button', $aElement); @@ -992,7 +1105,7 @@ class renderadminlte { * get a callout (box with coloered left border; has type, title + text) * https://adminlte.io/themes/v3/pages/UI/general.html * - * @param type $aOptions hash with keys for all options + * @param array $aOptions hash with keys for all options * >> styling * - type - one of [none]|danger|dark|info|primary|secondary|success|warning * - class - optional css class @@ -1002,19 +1115,21 @@ class renderadminlte { * - text - text: content of the card * @return string */ - public function getCallout($aOptions){ - $aOptions=$this->_ensureOptions('callout', $aOptions); - $sClass='callout' - . $this->_addClassValue($aOptions['type'], 'callout-') - . $this->_addClassValue($aOptions['class'], '') - .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) - ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '') - ; + public function getCallout(array $aOptions): string + { + $aOptions = $this->_ensureOptions('callout', $aOptions); + $sClass = 'callout' + . $this->_addClassValue($aOptions['type'], 'callout-') + . $this->_addClassValue($aOptions['class'], '') + . ($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) + ? ' ' . $this->_aValueMappings['shadow'][$aOptions['shadow']] : '') + ; return $this->addWrapper( - 'div', ['class'=>$sClass], - ($aOptions['title'] ? $this->_tag('h5', ['label'=>$aOptions['title']]) : '') - .($aOptions['text'] ? $this->_tag('p', ['label'=>$aOptions['text']]) : '') + 'div', + ['class' => $sClass], + ($aOptions['title'] ? $this->_tag('h5', ['label' => $aOptions['title']]) : '') + . ($aOptions['text'] ? $this->_tag('p', ['label' => $aOptions['text']]) : '') ); } @@ -1023,7 +1138,7 @@ class renderadminlte { * https://adminlte.io/docs/3.2/components/cards.html * https://adminlte.io/docs/3.2/javascript/card-widget.html * - * @param type $aOptions hash with keys for all options + * @param array $aOptions hash with keys for all options * >> styling * - variant: "default" - titlebar is colored * "outline" - a small stripe on top border is colored @@ -1048,67 +1163,70 @@ class renderadminlte { * - footer - text: footer of the card * @return string */ - public function getCard($aOptions){ - $aOptions=$this->_ensureOptions('card', $aOptions); + public function getCard(array $aOptions): string + { + $aOptions = $this->_ensureOptions('card', $aOptions); // css class prefixes based on "variant" value - $aVariants=[ - 'default' => 'card-', - 'outline' => 'card-outline card-', - 'solid' => 'bg-', + $aVariants = [ + 'default' => 'card-', + 'outline' => 'card-outline card-', + 'solid' => 'bg-', 'gradient' => 'bg-gradient-', ]; // window states: css class and toolbar buttons to add - $aStates=[ - 'collapsed'=>[ 'class'=>'collapsed-card', 'tool'=>'tb-expand'], - 'maximized'=>[ 'class'=>'maximized-card', 'tool'=>'tb-minimize'], + $aStates = [ + 'collapsed' => ['class' => 'collapsed-card', 'tool' => 'tb-expand'], + 'maximized' => ['class' => 'maximized-card', 'tool' => 'tb-minimize'], ]; - $aTools=[ - 'tb-collapse'=>'<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fa-solid fa-minus"></i></button>', - 'tb-expand'=>'<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fa-solid fa-plus"></i></button>', + $aTools = [ + 'tb-collapse' => '<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fa-solid fa-minus"></i></button>', + 'tb-expand' => '<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fa-solid fa-plus"></i></button>', - 'tb-maximize'=>'<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fa-solid fa-expand"></i></button>', - 'tb-minimize'=>'<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fa-solid fa-compress"></i></button>', + 'tb-maximize' => '<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fa-solid fa-expand"></i></button>', + 'tb-minimize' => '<button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fa-solid fa-compress"></i></button>', - 'tb-remove'=>'<button type="button" class="btn btn-tool" data-card-widget="remove"><i class="fa-solid fa-times"></i></button>', + 'tb-remove' => '<button type="button" class="btn btn-tool" data-card-widget="remove"><i class="fa-solid fa-times"></i></button>', ]; // print_r($aOptions); - $sVariantPrefix=isset($aVariants[$aOptions['variant']]) ? $aVariants[$aOptions['variant']] : $aVariants['default']; - $sClass='card' - . $this->_addClassValue($aOptions['type'], $sVariantPrefix) - .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) - ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '') - . $this->_addClassValue($aOptions['class'], '') - ; + $sVariantPrefix = isset($aVariants[$aOptions['variant']]) ? $aVariants[$aOptions['variant']] : $aVariants['default']; + $sClass = 'card' + . $this->_addClassValue($aOptions['type'], $sVariantPrefix) + . ($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) + ? ' ' . $this->_aValueMappings['shadow'][$aOptions['shadow']] : '') + . $this->_addClassValue($aOptions['class'], '') + ; // check window state - foreach($aStates as $sStatus=>$aStatus){ - if($aOptions['state']===$sStatus){ - $sClass.=' '.$aStatus['class']; - $aOptions[$aStatus['tool']]=1; + foreach ($aStates as $sStatus => $aStatus) { + if ($aOptions['state'] === $sStatus) { + $sClass .= ' ' . $aStatus['class']; + $aOptions[$aStatus['tool']] = 1; } } // add toolbar buttons - from given options or by window state - foreach($aTools as $sTool=>$sHtml){ - $aOptions['tools'].=($aOptions[$sTool] ? $sHtml : ''); + foreach ($aTools as $sTool => $sHtml) { + $aOptions['tools'] .= ($aOptions[$sTool] ? $sHtml : ''); } // build parts of the card - $sCardHeader=$aOptions['title'] - ? $this->addWrapper('div', ['class'=>'card-header'], - $this->_tag('h3', ['class'=>'card-title', 'label'=>$aOptions['title']]) - . ($aOptions['tools'] ? $this->_tag('div', ['class'=>'card-tools', 'label'=>$aOptions['tools']]) : '') + $sCardHeader = $aOptions['title'] + ? $this->addWrapper( + 'div', + ['class' => 'card-header'], + $this->_tag('h3', ['class' => 'card-title', 'label' => $aOptions['title']]) + . ($aOptions['tools'] ? $this->_tag('div', ['class' => 'card-tools', 'label' => $aOptions['tools']]) : '') ) : '' - ; + ; - $sCardBody=$this->_tag('div', ['class'=>'card-body', 'label'=>$aOptions['text']]); - $sCardFooter=$aOptions['footer'] ? $this->_tag('div', ['class'=>'card-footer', 'label'=>$aOptions['footer']]) : ''; + $sCardBody = $this->_tag('div', ['class' => 'card-body', 'label' => $aOptions['text']]); + $sCardFooter = $aOptions['footer'] ? $this->_tag('div', ['class' => 'card-footer', 'label' => $aOptions['footer']]) : ''; // merge all - return $this->addWrapper('div', ['class'=>$sClass], $sCardHeader.$sCardBody.$sCardFooter); + return $this->addWrapper('div', ['class' => $sClass], $sCardHeader . $sCardBody . $sCardFooter); } @@ -1117,7 +1235,7 @@ class renderadminlte { * A colored box with large icon, text and a value. * https://adminlte.io/docs/3.2/components/boxes.html * - * @param type $aOptions hash with keys for all options + * @param array $aOptions hash with keys for all options * styling: * - type - color of the box; one of [none]|danger|dark|info|primary|secondary|success|warning * - iconbg - background color or type of icon; use it for default type (type="") @@ -1131,39 +1249,44 @@ class renderadminlte { * - progresstext - text below progress bar * @return string */ - public function getInfobox($aOptions){ - $aOptions=$this->_ensureOptions('infobox', $aOptions); + public function getInfobox(array $aOptions): string + { + $aOptions = $this->_ensureOptions('infobox', $aOptions); // print_r($aOptions); - $sClass='info-box' - . $this->_addClassValue($aOptions['type'], 'bg-') - . $this->_addClassValue($aOptions['class'], '') - .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) - ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '') - ; - + $sClass = 'info-box' + . $this->_addClassValue($aOptions['type'], 'bg-') + . $this->_addClassValue($aOptions['class'], '') + . ($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) + ? ' ' . $this->_aValueMappings['shadow'][$aOptions['shadow']] : '') + ; + // build parts - $sIcon=$aOptions['icon'] + $sIcon = $aOptions['icon'] ? $this->addWrapper("span", [ - 'class'=>'info-box-icon'.($aOptions['iconbg'] ? ' bg-'.$aOptions['iconbg'] : '') - ], $this->_tag('i',['class'=>$aOptions['icon']])) + 'class' => 'info-box-icon' . ($aOptions['iconbg'] ? ' bg-' . $aOptions['iconbg'] : '') + ], $this->_tag('i', ['class' => $aOptions['icon']])) : '' - ; - $sContent=$this->addWrapper("div", ['class'=>'info-box-content'], + ; + $sContent = $this->addWrapper( + "div", + ['class' => 'info-box-content'], '' - . ($aOptions['text'] ? $this->_tag('span', ['class'=>'info-box-text', 'label'=>$aOptions['text']]) : '') - . ($aOptions['number'] ? $this->_tag('span', ['class'=>'info-box-number', 'label'=>$aOptions['number']]) : '') - . ($aOptions['progressvalue']!==false && $aOptions['progressvalue']!=='' - ? $this->addWrapper('div', ['class'=>'progress'], - $this->_tag('div', ['class'=>'progress-bar'. ($aOptions['iconbg'] ? ' bg-'.$aOptions['iconbg'] : ''), 'style'=>'width: '.(int)$aOptions['progressvalue'].'%' ]) - ) - . ($aOptions['progresstext'] ? $this->_tag('span', ['class'=>'progress-description', 'label'=>$aOptions['progresstext']]) : '' ) - : '' - ) + . ($aOptions['text'] ? $this->_tag('span', ['class' => 'info-box-text', 'label' => $aOptions['text']]) : '') + . ($aOptions['number'] ? $this->_tag('span', ['class' => 'info-box-number', 'label' => $aOptions['number']]) : '') + . ($aOptions['progressvalue'] !== false && $aOptions['progressvalue'] !== '' + ? $this->addWrapper( + 'div', + ['class' => 'progress'], + $this->_tag('div', ['class' => 'progress-bar' . ($aOptions['iconbg'] ? ' bg-' . $aOptions['iconbg'] : ''), 'style' => 'width: ' . (int) $aOptions['progressvalue'] . '%']) + ) + . ($aOptions['progresstext'] ? $this->_tag('span', ['class' => 'progress-description', 'label' => $aOptions['progresstext']]) : '') + : '' + ) ); // merge all - return $this->_tag('div', ['class'=>$sClass], $sIcon.$sContent); + return $this->_tag('div', ['class' => $sClass], $sIcon . $sContent); } @@ -1173,7 +1296,7 @@ class renderadminlte { * https://adminlte.io/docs/3.2/components/boxes.html * https://adminlte.io/themes/v3/pages/widgets.html * - * @param type $aOptions hash with keys for all options + * @param array $aOptions hash with keys for all options * styling: * - type - color of the box; one of [none]|danger|dark|info|primary|secondary|success|warning * - shadow - size of shadow; one of [none] (=default: between small and regular)|none|small|regular|large @@ -1185,42 +1308,50 @@ class renderadminlte { * - linktext- text below progress bar * @return string */ - public function getSmallbox($aOptions){ - $aOptions=$this->_ensureOptions('smallbox', $aOptions); + public function getSmallbox(array $aOptions): string + { + $aOptions = $this->_ensureOptions('smallbox', $aOptions); // print_r($aOptions); - $sClass='small-box' - . $this->_addClassValue($aOptions['type'], 'bg-') - . $this->_addClassValue($aOptions['class'], '') - .($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) - ? ' '.$this->_aValueMappings['shadow'][$aOptions['shadow']] : '') - ; - + $sClass = 'small-box' + . $this->_addClassValue($aOptions['type'], 'bg-') + . $this->_addClassValue($aOptions['class'], '') + . ($aOptions['shadow'] && isset($this->_aValueMappings['shadow'][$aOptions['shadow']]) + ? ' ' . $this->_aValueMappings['shadow'][$aOptions['shadow']] : '') + ; + // build parts - $sContent=$this->addWrapper("div", ['class'=>'inner'], + $sContent = $this->addWrapper( + "div", + ['class' => 'inner'], '' - . ($aOptions['number'] ? $this->_tag('h3', ['label'=>$aOptions['number']]) : '') - . ($aOptions['text'] ? $this->_tag('p', ['class'=>'info-box-text', 'label'=>$aOptions['text']]) : '') + . ($aOptions['number'] ? $this->_tag('h3', ['label' => $aOptions['number']]) : '') + . ($aOptions['text'] ? $this->_tag('p', ['class' => 'info-box-text', 'label' => $aOptions['text']]) : '') ); - $sIcon=$aOptions['icon'] - ? $this->addWrapper("div", ['class'=>'icon'], - $this->_tag('i',['class'=>$aOptions['icon']])) + $sIcon = $aOptions['icon'] + ? $this->addWrapper( + "div", + ['class' => 'icon'], + $this->_tag('i', ['class' => $aOptions['icon']]) + ) : '' - ; - $sFooter=($aOptions['url'] - ? $this->addWrapper("a", [ - 'class'=>'small-box-footer', - 'href'=>$aOptions['url'], + ; + $sFooter = ($aOptions['url'] + ? $this->addWrapper( + "a", + [ + 'class' => 'small-box-footer', + 'href' => $aOptions['url'], ], '' . ($aOptions['linktext'] ? $aOptions['linktext'] : $aOptions['url']) . ' ' - . $this->_tag('i',['class'=>'fa-solid fa-arrow-circle-right']) + . $this->_tag('i', ['class' => 'fa-solid fa-arrow-circle-right']) ) : '' ); // merge all - return $this->_tag('div', ['class'=>$sClass], $sContent.$sIcon.$sFooter); + return $this->_tag('div', ['class' => $sClass], $sContent . $sIcon . $sFooter); } // ---------------------------------------------------------------------- @@ -1230,120 +1361,229 @@ class renderadminlte { // ---------------------------------------------------------------------- - public function getHorizontalFormElement($sInput, $sLabel=false, $sId=false){ + /** + * Generates a horizontal form element with a label, input, and optional hint. + * + * @param string $sInput The HTML input element to be rendered. + * @param string $sLabel The label for the input element. + * @param string $sId The ID attribute for the label and input elements. + * @param string $sHint An optional hint to be displayed below the input element. + * @return string The generated HTML for the horizontal form element. + */ + public function getHorizontalFormElement(string $sInput, string $sLabel = '', string $sId = '', string $sHint=''): string + { return '<div class="form-group row">' - . '<label for="' . $sId . '" class="col-sm-2 col-form-label">' . $sLabel . '</label>' - . '<div class="col-sm-10">'.$sInput.'</div>' - . '</div>' - ; + . '<label for="' . $sId . '" class="col-sm-2 col-form-label">' . $sLabel . '</label>' + . '<div class="col-sm-10">' + . ($sHint + ? '<div class="text-navy hint">' . $sHint . '</div>' + : '') + . $sInput + . '</div>' + . '</div>' + ; } /** * return a text input field: * https://adminlte.io/themes/v3/pages/forms/general.html * - * @param type $aOptions hash with keys for all options + * @param array $aOptions hash with keys for all options * styling: * - type - field type: text, email, password, hidden and all other html 5 input types - * content + * content * - label - label tag * - name - name attribute for sending form * - value - value in field + * more: + * - hint - hint to be displayed above the field + * If not set, no hint is displayed. + * css for ".row .hint" to customize look and feel * @return string */ - public function GetFormInput($aOptions){ + public function GetFormInput(array $aOptions): string + { // $aOptions=$this->_ensureOptions('input', $aOptions); - $aElement=$aOptions; - $aElement['class']='' + $aElement = $aOptions; + $aElement['class'] = '' . 'form-control ' - . (isset($aOptions['class']) ? $aOptions['class']: '') - ; - $sFormid=(isset($aOptions['id']) - ? $aOptions['id'] - : (isset($aOptions['name']) ? $aOptions['name'] : 'field' ).'-'.md5(microtime(true)) + . (isset($aOptions['class']) ? $aOptions['class'] : '') + ; + $sFormid = (isset($aOptions['id']) + ? $aOptions['id'] + : (isset($aOptions['name']) ? $aOptions['name'] : 'field') . '-' . md5(microtime(true)) ); + $aElement['id'] = $sFormid; - $sLabel=isset($aOptions['label']) ? $aOptions['label'] : ''; - $sPrepend=''; - $sAppend=''; + $sLabel = isset($aOptions['label']) ? $aOptions['label'] : ''; + $sHint = isset($aOptions['hint']) ? $aOptions['hint'] : ''; + $sPrepend = ''; + $sAppend = ''; - if(isset($aOptions['prepend']) && $aOptions['prepend']){ - $sWrapperclass='input-group'; - $sPrepend=$this->_tag('div',[ 'class'=>'input-group-prepend'], - $this->_tag('span', ['class'=>'input-group-text'] , $aOptions['prepend']) + if (isset($aOptions['prepend']) && $aOptions['prepend']) { + $sWrapperclass = 'input-group'; + $sPrepend = $this->_tag( + 'div', + ['class' => 'input-group-prepend'], + $this->_tag('span', ['class' => 'input-group-text'], $aOptions['prepend']) ); } - if(isset($aOptions['append']) && $aOptions['append']){ - $sWrapperclass='input-group'; - $sAppend=$this->_tag('div',[ 'class'=>'input-group-append'], - $this->_tag('span', ['class'=>'input-group-text'] , $aOptions['append']) + if (isset($aOptions['append']) && $aOptions['append']) { + $sWrapperclass = 'input-group'; + $sAppend = $this->_tag( + 'div', + ['class' => 'input-group-append'], + $this->_tag('span', ['class' => 'input-group-text'], $aOptions['append']) ); - } + } - $aElement['id']=$sFormid; - foreach(['_infos', 'label', 'append', 'prepend', 'debug'] as $sDeleteKey){ - if(isset($aElement[$sDeleteKey])){ + foreach (['_infos', 'label', 'append', 'prepend', 'debug'] as $sDeleteKey) { + if (isset($aElement[$sDeleteKey])) { unset($aElement[$sDeleteKey]); } } // return data - switch($aElement['type']){ + switch ($aElement['type']) { case 'checkbox': case 'radio': - $aElement['class']=str_replace('form-control ', 'form-check-input', $aElement['class']); - return $this->_tag('div' , ['class'=>'form-check'], - $this->_tag('input', $aElement, '', false).$this->_tag('label', ['for'=>$sFormid, 'label'=>$sLabel ], '') + $aElement['class'] = str_replace('form-control ', 'form-check-input', $aElement['class']); + $aElement['title'] = $aElement['title'] ?? $sHint; + return $this->_tag( + 'div', + ['class' => 'form-check'], + $this->_tag('input', $aElement, '', false) . $this->_tag('label', ['for' => $sFormid, 'label' => $sLabel], '') ); break; case 'hidden': + $aElement['title'] = $aElement['title'] ?? $sHint; return $this->_tag('input', $aElement, '', false); break; - default: return $this->getHorizontalFormElement( - $sPrepend.$this->_tag('input', $aElement, '', false).$sAppend, - $sLabel, - $sFormid - ); + default: + return $this->getHorizontalFormElement( + $sPrepend . $this->_tag('input', $aElement, '', false) . $sAppend, + $sLabel, + $sFormid, + $sHint + ); } } /** - * return a textare field .. or html editor using summernote - * @param type $aOptions hash with keys for all options + * return a textarea field .. or html editor using summernote + * @param array $aOptions hash with keys for all options * styling: * - type - field type: [none]|html * content * - label - label tag * - name - name attribute for sending form * - value - value in + * more: + * - hint - hint to be displayed above the field + * If not set, no hint is displayed. + * css for ".row .hint" to customize look and feel * @return string */ - public function getFormTextarea($aOptions){ + public function getFormTextarea(array $aOptions): string + { // $aOptions=$this->_ensureOptions('textarea', $aOptions); - $aElement=$aOptions; - $aElement['class']='' + $aElement = $aOptions; + $aElement['class'] = '' . 'form-control ' - . ((isset($aOptions['type']) && $aOptions['type']=='html' )? 'summernote ': '') - . (isset($aOptions['class']) ? $aOptions['class']: '') + . ((isset($aOptions['type']) && $aOptions['type'] == 'html') ? 'summernote ' : '') + . (isset($aOptions['class']) ? $aOptions['class'] : '') ; - - $sLabel=isset($aOptions['label']) ? $aOptions['label'] : ''; - $sFormid=(isset($aOptions['id']) - ? $aOptions['id'] - : (isset($aOptions['name']) ? $aOptions['name'] : 'field' ).'-'.md5(microtime(true)) + $sFormid = (isset($aOptions['id']) + ? $aOptions['id'] + : (isset($aOptions['name']) ? $aOptions['name'] : 'field') . '-' . md5(microtime(true)) ); - $value=isset($aOptions['value']) ? $aOptions['value']: ''; - foreach(['_infos', 'label', 'debug','type', 'value'] as $sDeleteKey){ - if(isset($aElement[$sDeleteKey])){ + $sLabel = isset($aOptions['label']) ? $aOptions['label'] : ''; + $sHint = isset($aOptions['hint']) ? $aOptions['hint'] : ''; + $aElement['id'] = $sFormid; + + $value = isset($aOptions['value']) ? $aOptions['value'] : ''; + + foreach (['_infos', 'label', 'debug', 'type', 'value'] as $sDeleteKey) { + if (isset($aElement[$sDeleteKey])) { unset($aElement[$sDeleteKey]); } } return $this->getHorizontalFormElement( - $this->_tag('textarea', $aElement, $value), - $sLabel, - $sFormid + $this->_tag('textarea', $aElement, $value), + $sLabel, + $sFormid, + $sHint + ); + + } + + /** + * return a select box field + * @param array $aOptions hash with keys for all options + * option fields + * - options - array of options with keys per item: + * - value - value in the option + * - label - visible text in the option + * other keys are attributes in the option + * styling: + * - bootstrap-select - set true to enable select + * box with bootstrap-select and + * live search + * - class - css class + * select tag + * - label - label tag + * - name - name attribute for sending form + * other keys are attributes in the select + * more: + * - hint - hint to be displayed above the field + * If not set, no hint is displayed. + * css for ".row .hint" to customize look and feel + * @return string + */ + public function getFormSelect(array $aOptions): string + { + $aElement = $aOptions; + $aElement['class'] = '' + . 'form-control ' + . (isset($aOptions['class']) ? $aOptions['class'] . ' ' : '') + . (isset($aOptions['bootstrap-select']) ? 'selectpicker ' : '') //$aOptions + ; + if (isset($aOptions['bootstrap-select']) && $aOptions['bootstrap-select']) { + $aElement['data-live-search'] = "true"; + } + + $sFormid = (isset($aOptions['id']) + + ? $aOptions['id'] + : (isset($aOptions['name']) ? $aOptions['name'] : 'field') . '-' . md5(microtime(true)) + ); + $aElement['id'] = $sFormid; + $sLabel = isset($aOptions['label']) ? $aOptions['label'] : ''; + $sHint = isset($aOptions['hint']) ? $aOptions['hint'] : ''; + + $sOptionTags = ''; + foreach ($aOptions['options'] as $aField) { + $optionText = $aField['label']; + unset($aField['label']); + $sOptionTags .= $this->_tag('option', $aField, $optionText) . "\n"; + } + + foreach (['_infos', 'label', 'debug', 'type', 'value', 'options'] as $sDeleteKey) { + if (isset($aElement[$sDeleteKey])) { + unset($aElement[$sDeleteKey]); + } + } + return $this->getHorizontalFormElement( + $this->_tag( + 'div', + ['class' => 'form-group'], + $this->_tag('select', $aElement, $sOptionTags) + ), + $sLabel, + $sFormid, + $sHint ); } @@ -1358,55 +1598,62 @@ class renderadminlte { * return a box with tabbed content * @param array $aOptions hash with keys for all options * - tabs {array} key=tab label; value=content - * @retunr string|array + * @param bool $asArray optional flag: return hash with keys or as string + * @retunr bool|string|array */ - public function getTabbedContent($aOptions, $asArray=false){ + public function getTabbedContent(array $aOptions, bool $asArray = false): bool|string|array + { static $iTabCounter; - if( ! isset($aOptions['tabs']) || ! is_array($aOptions['tabs']) ){ + if (!isset($aOptions['tabs']) || !is_array($aOptions['tabs'])) { return false; } - if (!isset($iTabCounter)){ - $iTabCounter=1; + if (!isset($iTabCounter)) { + $iTabCounter = 1; } else { - $iTabCounter++; + $iTabCounter++; } - $id='tab-content-'.$iTabCounter; - $iCounter=0; + $id = 'tab-content-' . $iTabCounter; + $iCounter = 0; - $sTabs=''; - $sContent=''; - foreach($aOptions['tabs'] as $sLabel => $sTabContent){ + $sTabs = ''; + $sContent = ''; + foreach ($aOptions['tabs'] as $sLabel => $sTabContent) { $iCounter++; - $sTabId=$id.'-tabitem-'.$iCounter.'-tab'; - $sContentId=$id.'-tabitem-'.$iCounter.'-content'; - - $sTabs.=$this->_tag('li', ['class'=>'nav-item'], - $this->_tag('a', [ - 'class'=>'nav-link'.($iCounter==1 ? ' active' : '' ), - 'id'=>$sTabId, - 'data-toggle'=>'tab', - 'href'=>'#'.$sContentId, - 'role'=>'tab', - 'aria-controls'=>'custom-tabs-one-profile', - 'aria-selected'=>($iCounter==1 ? true : false ), - ], - $sLabel) + $sTabId = $id . '-tabitem-' . $iCounter . '-tab'; + $sContentId = $id . '-tabitem-' . $iCounter . '-content'; + + $sTabs .= $this->_tag( + 'li', + ['class' => 'nav-item'], + $this->_tag( + 'a', + [ + 'class' => 'nav-link' . ($iCounter == 1 ? ' active' : ''), + 'id' => $sTabId, + 'data-toggle' => 'tab', + 'href' => '#' . $sContentId, + 'role' => 'tab', + 'aria-controls' => 'custom-tabs-one-profile', + 'aria-selected' => ($iCounter == 1 ? true : false), + ], + $sLabel + ) ); - $sContent.=$this->_tag('div', [ - 'class'=>'tab-pane fade'.($iCounter==1 ? ' active show' : ''), - 'id'=>$sContentId, - 'role'=>'tabpanel', - 'aria-labelledby'=>$sTabId, - ], $sTabContent); + $sContent .= $this->_tag('div', [ + 'class' => 'tab-pane fade' . ($iCounter == 1 ? ' active show' : ''), + 'id' => $sContentId, + 'role' => 'tabpanel', + 'aria-labelledby' => $sTabId, + ], $sTabContent); } - $sTabs=$this->_tag('ul', ['class'=>'nav nav-tabs', 'role'=>'tablist'], $sTabs); - $sContent=$this->_tag('div', ['class'=>'tab-content'], $sContent); + $sTabs = $this->_tag('ul', ['class' => 'nav nav-tabs', 'role' => 'tablist'], $sTabs); + $sContent = $this->_tag('div', ['class' => 'tab-content'], $sContent); return $asArray - ? ['tabs'=>$sTabs, 'content'=>$sContent] + ? ['tabs' => $sTabs, 'content' => $sContent] : $sTabs . $sContent - ; + ; } } diff --git a/public_html/deployment/classes/rollout.interface.php b/public_html/deployment/classes/rollout.interface.php index 5b973b933d54f4dcb96bf4554cfb99827c6df57e..888e14edbf331457600c632425478a456c5756f6 100644 --- a/public_html/deployment/classes/rollout.interface.php +++ b/public_html/deployment/classes/rollout.interface.php @@ -3,71 +3,101 @@ * INTERFACE for rollout plugins * * @author hahn + * + * Axel <axel.hahn@unibe.ch> + * 2024-08-29 Axel php8 only; added variable types */ -interface iRolloutplugin { +interface iRolloutplugin +{ // ---------------------------------------------------------------------- // VERIFY // ---------------------------------------------------------------------- - + /** - * check requirements if the plugin could work + * Get an array with shell commands to check requirements if the plugin + * can work + * + * @return array */ - public function checkRequirements(); - + public function checkRequirements(): array; + /** - * check access to a deploy target + * Get an array with shell commands to check access to a deploy target + * + * @return array */ - public function checkConnectionToTarget(); + public function checkConnectionToTarget(): array; // ---------------------------------------------------------------------- // SETTER // ---------------------------------------------------------------------- - + // ---------------------------------------------------------------------- // GETTER // ---------------------------------------------------------------------- /** - * get configuration for the project .. or more specifi for a given phase + * Get configuration array for the project .. or more specific for a given phase + * * @param string $sPhase * @param boolean $bMask Flag for public output; if true then mask your secrets * @return array */ - public function getConfig($sPhase=false, $bMask=false); - + public function getConfig(string $sPhase = '', bool $bMask = false): array; + /** - * get an array with shell commands to execute + * Get an array with shell commands to execute for deployment of built file + * * @param string $sPhase * @param boolean $bMask Flag for public output; if true then mask your secrets * @return array */ - public function getDeployCommands($sPhase, $bMask=false); + public function getDeployCommands(string $sPhase, bool $bMask = false): array; /** - * get name of plugin as string ... language specific + * Get name of plugin as string ... language specific + * @return string */ - public function getName(); - + public function getName(): string; + /** - * get description of plugin as string ... language specific + * Get description of plugin as string ... language specific + * @return string */ - public function getDescription(); - + public function getDescription(): string; + /** - * get array of data in info.js + * Get array of data in info.js + * @return array */ - public function getPluginInfos(); + public function getPluginInfos(): array; + // ---------------------------------------------------------------------- // RENDERER // ---------------------------------------------------------------------- - public function renderFormdata4Project(); - public function renderFormdata4Phase($sPhase); + + /** + * form renderer: show + * - formvars for project OR + * - a single message that no configuration items exists + * + * @return string + */ + public function renderFormdata4Project(): string; + + /** + * form renderer: show configuration for a given phase + + * @param string $sPhase phaese; one of preview|stage|live + * @return string + */ + public function renderFormdata4Phase(string $sPhase): string; // ---------------------------------------------------------------------- // ACTIONS // ---------------------------------------------------------------------- - + } diff --git a/public_html/deployment/classes/rollout_base.class.php b/public_html/deployment/classes/rollout_base.class.php index c8b1b214131f65b9f24416041bb7e11bfcc835ba..fde105372f91d0e34784af8476cfecc1d769b5dd 100644 --- a/public_html/deployment/classes/rollout_base.class.php +++ b/public_html/deployment/classes/rollout_base.class.php @@ -1,15 +1,19 @@ <?php require_once 'rollout.interface.php'; -require_once __DIR__.'/../../vendor/axelhahn/ahcache/cache.class.php'; +require_once __DIR__ . '/../../vendor/axelhahn/ahcache/cache.class.php'; /** * rollout_base class that will be extended in a rollout plugin * see deployment/plugins/rollout/* * * @author axel + * + * Axel <axel.hahn@unibe.ch> + * 2024-08-29 Axel php8 only; added variable types; short array syntax */ -class rollout_base implements iRolloutplugin{ - +class rollout_base implements iRolloutplugin +{ + // --------------------------------------------------------------- // VARIABLES // --------------------------------------------------------------- @@ -18,58 +22,64 @@ class rollout_base implements iRolloutplugin{ * settings in the config structore for global and project based config * @var string */ - protected $_sPluginId='UNSET'; + protected string $_sPluginId = 'UNSET'; + /** - * data with plugin infos (read from info.json) + * Data with plugin infos (read from info.json) * @var array */ - protected $_aPlugininfos=false; - + protected array $_aPlugininfos = []; + /** - * array with translation texts - * @var type + * Array with translation texts + * @var array */ - protected $_aLang=false; + protected array $_aLang = []; /** * set language; 2 letter code, i.e. "de"; default language is "en" ; a * file "lang_en.json" is required in the plugin dir * @var string */ - protected $_sFallbackLang = 'en-en'; + protected string $_sFallbackLang = 'en-en'; /** * set language; 2 letter code, i.e. "de"; default language is "en" ; a * file "lang_en.json" is required in the plugin dir * @var string */ - protected $_sLang = 'en-en'; - + protected string $_sLang = 'en-en'; + /** * string with phase of project; one of preview|stage|live - * @var type + * @var string */ - protected $_sPhase = false; - + protected string $_sPhase = ''; + /** * global configuration of the rollout plugin * @var array */ - protected $_aCfgGlobal = false; + protected array $_aCfgGlobal = []; + /** * configuration of the project * @var array */ - protected $_aCfgProject = false; - - protected $_sNamePrefix4Project=false; // set in constructor - protected $_sNamePrefix4Phase=false; // set in constructor - + protected array $_aCfgProject = []; + + /* + UNUSED + protected string $_sNamePrefix4Project = ''; // set in constructor + protected string $_sNamePrefix4Phase = ''; // set in constructor + */ + // --------------------------------------------------------------- // CONSTRUCTOR // --------------------------------------------------------------- /** + * Constructor * initialize rollout plugin * @param array $aParams hash with those possible keys * lang string language, i.e. 'de' @@ -79,259 +89,269 @@ class rollout_base implements iRolloutplugin{ * for project and all phases * @return boolean */ - public function __construct($aParams) { - + public function __construct($aParams) + { + // set current plugin id - taken from plugin directory name above - $oReflection=new ReflectionClass($this); - $this->_sPluginId=basename(dirname($oReflection->getFileName())); - + $oReflection = new ReflectionClass($this); + $this->_sPluginId = basename(dirname($oReflection->getFileName())); + // ----- init language - if (isset($aParams['lang'])){ + if (isset($aParams['lang'])) { $this->setLang($aParams['lang']); } else { $this->setLang(); } - + // ----- init phase - if (isset($aParams['phase'])){ + if (isset($aParams['phase'])) { $this->setPhase($aParams['phase']); } // ----- init global config - if (isset($aParams['globalcfg'])){ + if (isset($aParams['globalcfg'])) { $this->setGlobalConfig($aParams['globalcfg']); } // ----- init project config - if (isset($aParams['projectcfg'])){ + if (isset($aParams['projectcfg'])) { $this->setProjectConfig($aParams['projectcfg']); } - return true; } - + // --------------------------------------------------------------- // FORM HELPER // --------------------------------------------------------------- /** - * get a string for a prefix for name attribute in form vars. + * Get a string for a prefix for name attribute in form vars. * It is important to store the value in the wanted structure. * - * @param type $sPhase - * @return type + * @param string $sPhase + * @return string */ - protected function _getNamePrefix($sPhase=false){ + protected function _getNamePrefix(string $sPhase = ''): string + { return ($sPhase - ? 'phases['.$sPhase.'][plugins][rollout]['.$this->getId().']' - : 'plugins[rollout]['.$this->getId().']' + ? 'phases[' . $sPhase . '][plugins][rollout][' . $this->getId() . ']' + : 'plugins[rollout][' . $this->getId() . ']' ); } - + /** - * get Data from a callback function and store it in a cache + * Get Data from a callback function and store it in a cache * The response type depends on the callback function * * @param string $sFunctionname name of the callback function * @param string $sKey name of the key; "project" or name of phase - * @param integr $iTtl ttl value = how many seconds to use cache - * @param integr $iTtlOnError ttl value = how many seconds to use cache if there was no response - * @return type + * @param integer $iTtl ttl value = how many seconds to use cache + * @param integer $iTtlOnError ttl value = how many seconds to use cache if there was no response + * @return mixed */ - protected function _getCallback($sFunctionname, $sKey, $iTtl=15,$iTtlOnError=10){ - $oCache=new AhCache('rollout-'.$this->getId(), 'callback-'.$sFunctionname.'-'.$sKey); - if($oCache->isExpired()){ - $aMydata= call_user_func(array($this, $sFunctionname)); + protected function _getCallback(string $sFunctionname, string $sKey, int $iTtl = 15, int $iTtlOnError = 10): mixed + { + $oCache = new AhCache('rollout-' . $this->getId(), 'callback-' . $sFunctionname . '-' . $sKey); + if ($oCache->isExpired()) { + $aMydata = call_user_func([$this, $sFunctionname]); // echo "$sFunctionname fresh ".($aMydata ? "OK": "false")." - storing for $iTtl sec<br>"; $oCache->write($aMydata, ($aMydata ? $iTtl : $iTtlOnError)); } else { // echo "$sFunctionname from cache ... ".$oCache->iExpired()." sec <br>"; - $aMydata=$oCache->read(); + $aMydata = $oCache->read(); } // echo '<pre>'.print_r($aMydata, 1).'</pre>'; die(__METHOD__); return $aMydata; } + /** - * render a form by given form elementes + * Get Html code for a form by given form elementes + * * @param array $aFormdata array of form elements * @param string $sKey part of the identifier used in id of the input field * @return string */ - protected function _renderForm($aFormdata, $sKey){ + protected function _renderForm(array $aFormdata, string $sKey): string + { static $i; - if (!isset($i)){ - $i=0; + if (!isset($i)) { + $i = 0; } - $sReturn=''; - $sKeyPrefix=$this->getId().'_'.$sKey; - + $sReturn = ''; + $sKeyPrefix = $this->getId() . '_' . $sKey; + $oForm = new formgen(); foreach ($aFormdata as $elementData) { - $elementKey=$sKeyPrefix.'_'.$i++; - $sReturn.=$oForm->renderHtmlElement($elementKey, $elementData); + $elementKey = $sKeyPrefix . '_' . $i++; + $sReturn .= $oForm->renderHtmlElement($elementKey, $elementData); } return $sReturn; } - + /** - * render form fields for global plugin variables + * Get Html code for form fields for global plugin variables + * * @param string $sScope scope of vars ... one of global|project|phase * @param string $sPhase optional: render global vars in a phase; if no phase was set it renders form fields for project based settings * @return string */ - protected function _renderForm4Vars($sScope, $sPhase=false){ - $sReturn=''; - + protected function _renderForm4Vars(string $sScope, string $sPhase = ''): string + { + $sReturn = ''; + // test vars from info.json file - $aInfos=$this->getPluginInfos(); - if(!isset($aInfos['vars'][$sScope]) || !count($aInfos['vars'][$sScope])){ + $aInfos = $this->getPluginInfos(); + if (!isset($aInfos['vars'][$sScope]) || !count($aInfos['vars'][$sScope])) { return ''; } - - $sKey=($sPhase ? 'phase_'.$sPhase : 'project'); - $sPrefixName=$this->_getNamePrefix($sPhase); - + + $sKey = ($sPhase ? 'phase_' . $sPhase : 'project'); + $sPrefixName = $this->_getNamePrefix($sPhase); + // set defaults - to be used in placeholder attribute // no phase = project level - take global defaults of ci config // on a phase - fetch merged cofig data of project - $aDefaultValues=($sPhase ? $this->getConfig() : $this->_aCfgGlobal); - $aDefaultSource=($sPhase ? 'project' : 'global'); - + $aDefaultValues = ($sPhase ? $this->getConfig() : $this->_aCfgGlobal); + $aDefaultSource = ($sPhase ? 'project' : 'global'); + // set defaults - to be used in value attribute - $aValues=($sPhase - ? $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()] - : $this->_aCfgProject['plugins']['rollout'][$this->getId()] + $aValues = ($sPhase + ? $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()] + : $this->_aCfgProject['plugins']['rollout'][$this->getId()] ); - - + + // create form fields - // $aFormdata[]=array('type' => 'markup','value' => '<br>'.$this->_t('section-override-'.$sScope.'-vars').':'); - $aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><h4>'.$this->getId() .' :: '. $sScope.'</h4>'); - - $sMiss=''; - foreach ($aInfos['vars'][$sScope] as $sVarname=>$aVarinfos){ - if ($sScope==='global' && !isset($this->_aCfgGlobal[$sVarname])){ - $sMiss.='- plugin var was not set in global CI config: "'.$sVarname.'".<br>'; + // $aFormdata[]=['type' => 'markup','value' => '<br>'.$this->_t('section-override-'.$sScope.'-vars').':']; + $aFormdata[] = ['type' => 'markup', 'value' => '<div style="style: clear: left;"></div><h4>' . $this->getId() . ' :: ' . $sScope . '</h4>']; + + $sMiss = ''; + foreach ($aInfos['vars'][$sScope] as $sVarname => $aVarinfos) { + if ($sScope === 'global' && !isset($this->_aCfgGlobal[$sVarname])) { + $sMiss .= '- plugin var was not set in global CI config: "' . $sVarname . '".<br>'; } - - - $sMyPlaceholder=(isset($aDefaultValues[$sVarname]) - ? htmlentities($aDefaultValues[$sVarname]) - : (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.') - ); - + + + $sMyPlaceholder = (isset($aDefaultValues[$sVarname]) + ? htmlentities($aDefaultValues[$sVarname]) + : (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.') + ); + // if a callback was set for this variable - if(isset($aVarinfos['callback'])){ - $aCallbackData=$this->_getCallback( - $aVarinfos['callback'], - (isset($aVarinfos['per_scope']) && $aVarinfos['per_scope'] ? $sKey : ''), - (isset($aVarinfos['ttl']) ? $aVarinfos['ttl'] : 60) + if (isset($aVarinfos['callback'])) { + $aCallbackData = $this->_getCallback( + $aVarinfos['callback'], + (isset($aVarinfos['per_scope']) && $aVarinfos['per_scope'] ? $sKey : ''), + (isset($aVarinfos['ttl']) ? $aVarinfos['ttl'] : 60) ); - if(!$aCallbackData){ - $aVarinfos['type']='text'; + if (!$aCallbackData) { + $aVarinfos['type'] = 'text'; } else { - $aEffectiveConfig=$this->getConfig($sPhase); + $aEffectiveConfig = $this->getConfig($sPhase); // echo $sKey.' ... '; print_r($aEffectiveConfig[$sVarname]); echo '<br>'; - + // mark entry as active ... for select and radio - if(isset($aEffectiveConfig[$sVarname]) && isset($aCallbackData[$aEffectiveConfig[$sVarname]])){ - $aCallbackData[$aEffectiveConfig[$sVarname]]['selected']='selected'; - $aCallbackData[$aEffectiveConfig[$sVarname]]['checked']='checked'; - $aCallbackData[$aEffectiveConfig[$sVarname]]['label'].=' ««'; - } elseif ($aVarinfos['type']==='select') { - $aCallbackData=array_merge(array('NO_SELECTED_ITEM_YET'=>array('value'=>'', 'label'=>'...')), $aCallbackData); + if (isset($aEffectiveConfig[$sVarname]) && isset($aCallbackData[$aEffectiveConfig[$sVarname]])) { + $aCallbackData[$aEffectiveConfig[$sVarname]]['selected'] = 'selected'; + $aCallbackData[$aEffectiveConfig[$sVarname]]['checked'] = 'checked'; + $aCallbackData[$aEffectiveConfig[$sVarname]]['label'] .= ' ««'; + } elseif ($aVarinfos['type'] === 'select') { + $aCallbackData = array_merge(['NO_SELECTED_ITEM_YET' => ['value' => '', 'label' => '...']], $aCallbackData); } - + // wenn value = defaultvalue, dann value auf '' setzen (damit bei Default vom Scope // davor ein Leerstring übergeben wird - analog onfocusout im Text Input - if (isset($aCallbackData[$aDefaultValues[$sVarname]])){ - $aCallbackData[$aDefaultValues[$sVarname]]['value']=''; - $aCallbackData[$aDefaultValues[$sVarname]]['label'].=' (*)'; - } + if (isset($aCallbackData[$aDefaultValues[$sVarname]])) { + $aCallbackData[$aDefaultValues[$sVarname]]['value'] = ''; + $aCallbackData[$aDefaultValues[$sVarname]]['label'] .= ' (*)'; + } // print_r($aCallbackData[$sVarname]); echo "<br>"; } // echo '<pre>'.$sCallbackfunktion .' = '. print_r($aMydata,1 ).'</pre>'; } switch ($aVarinfos['type']) { case "password": - $sMyPlaceholder=(isset($aDefaultValues[$sVarname]) - ? '******************************' - : $sMyPlaceholder - ); - $aFormdata[]=array( + $sMyPlaceholder = (isset($aDefaultValues[$sVarname]) + ? '******************************' + : $sMyPlaceholder + ); + $aFormdata[] = [ 'type' => $aVarinfos['type'], - 'name' => $sPrefixName.'['.$sVarname.']', - 'label' => $this->_t($sVarname.'-label'), - 'title' => $this->_t($sVarname.'-hint'), + 'name' => $sPrefixName . '[' . $sVarname . ']', + 'label' => $this->_t($sVarname . '-label'), + 'title' => $this->_t($sVarname . '-hint'), 'value' => (isset($aValues[$sVarname]) ? htmlentities($aValues[$sVarname]) : ''), // 'required' => 'required', 'validate' => 'isastring', // 'size' => 25, 'placeholder' => $sMyPlaceholder, 'autocomplete' => 'off', - ); + ]; break; case "select": case "radio": - $aOptions=$aCallbackData; - $aFormdata[]=array( + $aOptions = $aCallbackData; + $aFormdata[] = [ 'type' => $aVarinfos['type'], - 'name' => $sPrefixName.'['.$sVarname.']', - 'label' => $this->_t($sVarname.'-label'), - 'title' => $this->_t($sVarname.'-hint'), + 'name' => $sPrefixName . '[' . $sVarname . ']', + 'label' => $this->_t($sVarname . '-label'), + 'title' => $this->_t($sVarname . '-hint'), 'validate' => 'isastring', 'options' => $aOptions, - + // 'placeholder' => $sMyPlaceholder - ); + ]; break; case "text": - $aFormdata[]=array( + $aFormdata[] = [ 'type' => $aVarinfos['type'], - 'name' => $sPrefixName.'['.$sVarname.']', - 'label' => $this->_t($sVarname.'-label'), - 'ondblclick' => ($aDefaultValues[$sVarname] ? 'if (this.value==\'\') { this.value=\''.$aDefaultValues[$sVarname].'\' }' : ''), - 'onfocusout' => ($aDefaultValues[$sVarname] ? 'if (this.value==\''.$aDefaultValues[$sVarname].'\') { this.value=\'\' }' : ''), - 'title' => htmlentities($this->_t($sVarname.'-hint')."\n" - . ($this->_aCfgGlobal[$sVarname] ? '- global: '.$this->_aCfgGlobal[$sVarname]."\n" : '') - . ($this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname] ? '- project: '.$this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname]."\n" : '') - ) - , + 'name' => $sPrefixName . '[' . $sVarname . ']', + 'label' => $this->_t($sVarname . '-label'), + 'ondblclick' => ($aDefaultValues[$sVarname] ? 'if (this.value==\'\') { this.value=\'' . $aDefaultValues[$sVarname] . '\' }' : ''), + 'onfocusout' => ($aDefaultValues[$sVarname] ? 'if (this.value==\'' . $aDefaultValues[$sVarname] . '\') { this.value=\'\' }' : ''), + 'title' => htmlentities( + $this->_t($sVarname . '-hint') . "\n" + . ($this->_aCfgGlobal[$sVarname] ? '- global: ' . $this->_aCfgGlobal[$sVarname] . "\n" : '') + . ($this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname] ? '- project: ' . $this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname] . "\n" : '') + ) + , 'value' => (isset($aValues[$sVarname]) ? htmlentities($aValues[$sVarname]) : ''), // 'required' => 'required', 'validate' => 'isastring', // 'size' => 25, 'placeholder' => $sMyPlaceholder, 'autocomplete' => 'off', - ); + ]; break; default: - $sMiss.='- plugin var "'.$sVarname.'" was not rendered - its type "'.$aVarinfos['type'].'" is not supported in the general form renderer.<br>'; - break; + $sMiss .= '- plugin var "' . $sVarname . '" was not rendered - its type "' . $aVarinfos['type'] . '" is not supported in the general form renderer.<br>'; + break; } } - // $aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><br><br>'); + // $aFormdata[]=['type' => 'markup','value' => '<div style="style: clear: left;"></div><br><br>']; return $this->_renderForm($aFormdata, $sKey) - . ($sMiss - ? '<pre>WARNINGS:<br>'.$sMiss.'</pre>' . ($sScope==='global' ? $this -> renderCfgExample() : '' ) + . ($sMiss + ? '<pre>WARNINGS:<br>' . $sMiss . '</pre>' . ($sScope === 'global' ? $this->renderCfgExample() : '') : '' ) - ; + ; } - + /** - * get a translated text from lang_XX.json in plugin dir; + * Get a translated text from lang_XX.json in plugin dir; * If the key is missed it returns "[KEY :: LANG]" * * @see setLang() + * * @param string $sKey key to find in lang file * @return string */ - protected function _t($sKey){ + protected function _t(string $sKey): string + { return (isset($this->_aLang[$sKey]) && $this->_aLang[$sKey]) - ? $this->_aLang[$sKey] - : "[ $sKey :: $this->_sLang ]" + ? $this->_aLang[$sKey] + : "[ $sKey :: $this->_sLang ]" ; } @@ -345,125 +365,145 @@ class rollout_base implements iRolloutplugin{ * translated texts can be done with $this->_t("your_key") * * @see _t() + * * @param string $sLang language code, i.e. "de-de" * @return boolean */ - public function setLang($sLang=false){ - $this->_sLang=$sLang ? $sLang : $this->_sLang; - - $oReflection=new ReflectionClass($this); - $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sLang.'.json'; - if (!file_exists($sFile)){ - $sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sFallbackLang.'.json'; - $this->_sLang=$this->_sFallbackLang; + public function setLang(string $sLang = ''): bool + { + $this->_sLang = $sLang ? $sLang : $this->_sLang; + + $oReflection = new ReflectionClass($this); + $sFile = dirname($oReflection->getFileName()) . '/lang_' . $this->_sLang . '.json'; + if (!file_exists($sFile)) { + $sFile = dirname($oReflection->getFileName()) . '/lang_' . $this->_sFallbackLang . '.json'; + $this->_sLang = $this->_sFallbackLang; } - $this->_aLang=(file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang; + $this->_aLang = (file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang; return true; } - + /** * set a phase for automatic use GETTER methods + * + * @param string $sPhase name of the phase; one of preview|stage|live + * @return boolean */ - public function setPhase($sPhase){ - $this->_sPhase=$sPhase; + public function setPhase(string $sPhase): bool + { + $this->_sPhase = $sPhase; return true; } - + // ---------------------------------------------------------------------- // INTERFACE :: CHECKS // ---------------------------------------------------------------------- /** - * check requirements if the plugin could work + * Get an array with shell commands to check requirements if the plugin + * can work + * + * @return array */ - public function checkRequirements(){ - // no specific checks needed ... always true - return true; + public function checkRequirements(): array + { + // no specific checks needed ... always empty + return []; } /** - * check access to a deploy target + * Get an array with shell commands to check access to a deploy target + * + * @return array */ - public function checkConnectionToTarget(){ + public function checkConnectionToTarget(): array + { // do nothing ... always true - return true; + return []; } // ---------------------------------------------------------------------- // INTERFACE :: SETTER // ---------------------------------------------------------------------- - /** - * set Config ... by given global config of the current plugin - * @param array $aConfigArray + * Set Config ... by given global config of the current plugin + * + * @param array $aConfigArray complete array of all config data + * @return boolean */ - public function setGlobalConfig($aConfigArray){ - return $this->_aCfgGlobal=$aConfigArray; + public function setGlobalConfig(array $aConfigArray): bool + { + $this->_aCfgGlobal = $aConfigArray; + return true; } - - /** - * set Config ... by given project config + * Set Config ... by given project config + * + * @param array $aProjectConfigArray complete array of project config + * @return array */ - public function setProjectConfig($aProjectConfigArray){ - $this->_aCfgProject=$aProjectConfigArray; + public function setProjectConfig(array $aProjectConfigArray): array + { + $this->_aCfgProject = $aProjectConfigArray; // echo '<pre>'.print_r($aProjectConfigArray, 1).'</pre>'; // ----- ensure that the config structure exists // (it is easier fo handling in getConfig()) - if (!isset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId])){ + if (!isset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId])) { /* if (!isset($this->_aCfgProject['plugins']['rollout'])){ if (!isset($this->_aCfgProject['plugins'])){ - $this->_aCfgProject['plugins']=array(); + $this->_aCfgProject['plugins']=[]; } - $this->_aCfgProject['plugins']['rollout']=array(); + $this->_aCfgProject['plugins']['rollout']=[]; } * */ - $this->_aCfgProject['plugins']['rollout'][$this->_sPluginId]=array('INFO'=>'created'); + $this->_aCfgProject['plugins']['rollout'][$this->_sPluginId] = ['INFO' => 'created']; } - + // unset empty project values to get global values - foreach ($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId] as $sVarname=>$value){ - if ($value===''){ + foreach ($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId] as $sVarname => $value) { + if ($value === '') { unset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId][$sVarname]); } } - foreach (array_keys($this->_aCfgProject['phases']) as $sMyPhase){ - if (isset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()])){ - foreach($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()] as $sVarname=>$value){ - if ($value===''){ + foreach (array_keys($this->_aCfgProject['phases']) as $sMyPhase) { + if (isset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()])) { + foreach ($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()] as $sVarname => $value) { + if ($value === '') { unset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()][$sVarname]); } } } } - // TODO: + return $this->_aCfgProject; } - + // ---------------------------------------------------------------------- // INTERFACE :: GETTER // ---------------------------------------------------------------------- /** - * get configuration for the project .. or more specifi for a given phase + * Get configuration array for the project .. or more specific for a given phase + * * @param string $sPhase * @param boolean $bMask Flag for public output; if true then mask your secrets * @return array */ - public function getConfig($sPhase=false, $bMask=false){ + public function getConfig(string $sPhase = '', bool $bMask = false): array + { - $aReturn=array_merge($this->_aCfgGlobal, $this->_aCfgProject['plugins']['rollout'][$this->getId()]); - if($sPhase && isset($this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()])){ - $aReturn=array_merge($aReturn, $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()]); + $aReturn = array_merge($this->_aCfgGlobal, $this->_aCfgProject['plugins']['rollout'][$this->getId()]); + if ($sPhase && isset($this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()])) { + $aReturn = array_merge($aReturn, $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()]); } - if ($bMask && isset($aReturn['password'])){ - $aReturn['password']='**********'; + if ($bMask && isset($aReturn['password'])) { + $aReturn['password'] = '**********'; } return $aReturn; /* @@ -473,58 +513,67 @@ class rollout_base implements iRolloutplugin{ ; */ } - + /** - * get an array with shell commands to execute + * Get an array with shell commands to execute for deployment of built file + * * @param string $sPhase * @param boolean $bMask Flag for public output; if true then mask your secrets * @return array */ - public function getDeployCommands($sPhase, $bMask=false){ + public function getDeployCommands(string $sPhase, bool $bMask = false): array + { return [ - 'echo "ERROR: The method getDeployCommamds($sPhase) was not implemented in the rollout plugin ['.$this->getId().']"', + 'echo "ERROR: The method getDeployCommamds($sPhase) was not implemented in the rollout plugin [' . $this->getId() . ']"', 'exit 1' - ]; + ]; } - + /** - * get string with current ID + * Get string with current plugin ID + * * @return string */ - public function getId(){ + public function getId(): string + { return $this->_sPluginId; } - + /** - * get string with plugin name (taken from plugin language file) + * Get name of plugin as string ... language specific + * * @return string */ - public function getName(){ + public function getName(): string + { return $this->_t('plugin_name'); } - + /** - * get string with plugin description (taken from plugin language file) + * Get description of plugin as string ... language specific * @return string */ - public function getDescription(){ + public function getDescription(): string + { return $this->_t('description'); } + /** - * get array read from info.json - * @return type + * Get array of data in info.js + * @return array */ - public function getPluginInfos(){ + public function getPluginInfos(): array + { - if ($this->_aPlugininfos){ + if ($this->_aPlugininfos) { return $this->_aPlugininfos; } - - $oReflection=new ReflectionClass($this); - $sFile=dirname($oReflection->getFileName()) . '/info.json'; - $this->_aPlugininfos= (file_exists($sFile)) + + $oReflection = new ReflectionClass($this); + $sFile = dirname($oReflection->getFileName()) . '/info.json'; + $this->_aPlugininfos = (file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) - : array('error'=> 'unable to read info file ['.$sFile.'].') + : ['error' => 'unable to read info file [' . $sFile . '].'] ; return $this->_aPlugininfos; } @@ -532,71 +581,101 @@ class rollout_base implements iRolloutplugin{ // ---------------------------------------------------------------------- // INTERFACE :: RENDERER // ---------------------------------------------------------------------- - public function renderCfgExample(){ - $sReturn=''; - $sPre=' '; - - $aInfos=$this->getPluginInfos(); - $sReturn.='<pre>$aConfig = array( + + /** + * Get HTML code for example configuration + * @return string + */ + public function renderCfgExample() + { + $sReturn = ''; + $sPre = ' '; + + $aInfos = $this->getPluginInfos(); + $sReturn .= '<pre>$aConfig = [ ... - \'plugins\'=>array( + \'plugins\'=>[ ... // enabled rollout plugins - \'rollout\'=>array( + \'rollout\'=>[ ... <strong> - \''.$this->getId().'\'=>array( - // '.$this->getName().' - // '.$this->getDescription().' - '.PHP_EOL; - + \'' . $this->getId() . '\'=>[ + // ' . $this->getName() . ' + // ' . $this->getDescription() . ' + ' . PHP_EOL; + // add global vars - if(!isset($aInfos['vars']['global']) || !count($aInfos['vars']['global'])){ - $sReturn.=$sPre.'// this plugin has no global config vars'.PHP_EOL; + if (!isset($aInfos['vars']['global']) || !count($aInfos['vars']['global'])) { + $sReturn .= $sPre . '// this plugin has no global config vars' . PHP_EOL; } else { - foreach ($aInfos['vars']['global'] as $sVar=>$aItem){ - $sReturn.=$sPre.'// '.$this->_t($sVar.'-hint').PHP_EOL; - $sReturn.=$sPre.'\''.$sVar.'\'=>\''.(isset($this->_aCfgGlobal[$sVar]) ? $this->_aCfgGlobal[$sVar] : $aItem['default']).'\','.PHP_EOL; - $sReturn.=PHP_EOL; + foreach ($aInfos['vars']['global'] as $sVar => $aItem) { + $sReturn .= $sPre . '// ' . $this->_t($sVar . '-hint') . PHP_EOL; + $sReturn .= $sPre . '\'' . $sVar . '\'=>\'' . (isset($this->_aCfgGlobal[$sVar]) ? $this->_aCfgGlobal[$sVar] : $aItem['default']) . '\',' . PHP_EOL; + $sReturn .= PHP_EOL; } } - - $sReturn.=' - ), + + $sReturn .= ' + ], </strong> ... - ), + ], ... - ), -);</pre>'; + ], +];</pre>'; return $sReturn; } - protected function _renderMoreToggler($sContent){ - $sDivId='rollout-more-toggler-'.$this->getId().'-'.md5($sContent); + + /** + * Get html code for button [...] that shows and hides more information + * + * @param string $sContent + * @return string + */ + protected function _renderMoreToggler(string $sContent) + { + $sDivId = 'rollout-more-toggler-' . $this->getId() . '-' . md5($sContent); return '' - . '<button onclick="$(\'#'.$sDivId.'\').slideToggle(); return false;" class="btn btn-secondary"> ... </button>' - . '<div id="'.$sDivId.'" style="display: none;">' - . $sContent - . '</div>' - ; - + . '<button onclick="$(\'#' . $sDivId . '\').slideToggle(); return false;" class="btn btn-secondary"> ... </button>' + . '<div id="' . $sDivId . '" style="display: none;">' + . $sContent + . '</div>' + ; + } - public function renderFormdata4Project() { + + /** + * Override general form renderer: show + * - formvars for project + * + * @return string + */ + public function renderFormdata4Project(): string + { return '' - . $this->_renderForm4Vars('project', false) - . $this->_renderForm4Vars('global', false) - // . $this->_renderFormProjectVars($this->_sNamePrefix4Project, false) - // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' - // . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' - // .'<pre>DEBUG: $this->_aCfgProject ... plugin = '.print_r($this->_aCfgProject, 1).'</pre>' + . $this->_renderForm4Vars('project', false) + . $this->_renderForm4Vars('global', false) + // . $this->_renderFormProjectVars($this->_sNamePrefix4Project, false) + // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' + // . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' + // .'<pre>DEBUG: $this->_aCfgProject ... plugin = '.print_r($this->_aCfgProject, 1).'</pre>' ; } - public function renderFormdata4Phase($sPhase){ + + /** + * override of form renderer: show configuration for a given phase + + * @param string $sPhase phaese; one of preview|stage|live + * @return string + */ + public function renderFormdata4Phase(string $sPhase): string + { static $iCounter; - if(!isset($iCounter)){ - $iCounter=0; + if (!isset($iCounter)) { + $iCounter = 0; } - $sDivId='rollout-override-div-'.$this->getId().'-'.$sPhase.'-'.$iCounter; + $sDivId = 'rollout-override-div-' . $this->getId() . '-' . $sPhase . '-' . $iCounter; return '' . $this->_renderForm4Vars('phase', $sPhase) . $this->_renderMoreToggler( @@ -608,6 +687,6 @@ class rollout_base implements iRolloutplugin{ // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' // . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' // . '<pre>DEBUG: PHASE settings - $this->getConfig("'.$sMyPhase.'") = ' . print_r($this->getConfig($sMyPhase), 1) . '</pre>' - ; + ; } } diff --git a/public_html/deployment/classes/sws.class.php b/public_html/deployment/classes/sws.class.php index 1405b255e3a7d6a4975a9df17c2425fecc7fcac9..19194e0a479ae2652ed13dd37a915655909d1006 100644 --- a/public_html/deployment/classes/sws.class.php +++ b/public_html/deployment/classes/sws.class.php @@ -141,7 +141,6 @@ class sws { if (is_array($aKnownClasses)) { $this->setConfig($aKnownClasses); } - return true; } // ---------------------------------------------------------------------- diff --git a/public_html/deployment/classes/user.class.php b/public_html/deployment/classes/user.class.php index 187bbdd52dfb1ec106fabf8f34d0270ae6f08753..adb85a9ee86b7a314efad7977ba9df086e68af02 100644 --- a/public_html/deployment/classes/user.class.php +++ b/public_html/deployment/classes/user.class.php @@ -5,106 +5,126 @@ * This class is used in the base class * * @author hahn + * + * Axel <axel.hahn@unibe.ch> + * 2024-08-29 Axel php8 only; added variable types; use short array syntax */ -class user { - +class user +{ + /** * login name of the current user * @var string */ - private $_sUsername=false; - + private string $_sUsername = ''; + /** * list of groups of the current user * @var array */ - private $_aUserGroups=array(); - + private array $_aUserGroups = []; + /** * list of roles based on the groups * @var array */ - private $_aUserPermmissions=array(); - + private array $_aUserPermmissions = []; + /** * list of projects the current user is involved in * @var array */ - private $_aProjects=array(); - + private $_aProjects = []; + /** * name of the last checked role * @var string */ - private $_sLastCheckedPermission=false; - + private $_sLastCheckedPermission = false; + /** + * Constructor * init user with optional given user - * @param type $sUser + * + * @param string $sUser username to set */ - public function __construct($sUser=false){ + public function __construct(string $sUser = '') + { $this->setUser($sUser); } - - + + // ---------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------- - - + + /** - * get string with detected user from current session / basic auth / cli access + * Get string with detected user from current session / basic auth / cli access + * * @return string */ - private function _autoDetectUser(){ - $sUser=false; - if (isset($_SESSION) && isset($_SESSION["PHP_AUTH_USER"])){ - $sUser=$_SESSION["PHP_AUTH_USER"]; + private function _autoDetectUser(): string + { + $sUser = ''; + if (isset($_SESSION) && isset($_SESSION["PHP_AUTH_USER"])) { + $sUser = $_SESSION["PHP_AUTH_USER"]; } - if (!$sUser && isset($_SERVER["PHP_AUTH_USER"])){ - $sUser=$_SERVER["PHP_AUTH_USER"]; + if (!$sUser && isset($_SERVER["PHP_AUTH_USER"])) { + $sUser = $_SERVER["PHP_AUTH_USER"]; } if (php_sapi_name() == "cli") { - $sUser="cliadmin"; + $sUser = "cliadmin"; } return $sUser; } - // UNUSED SO FAR - private function _getUser2Projects(){ - $sFile=__DIR__ . '/../../../config/inc_user2projects.php'; + /** + * UNUSED SO FAR + * Idea: limit user access to a set of projects + */ + private function _getUser2Projects() + { + $sFile = __DIR__ . '/../../../config/inc_user2projects.php'; return file_exists($sFile) ? require $sFile : [] ; } - - private function _getUser2Roles(){ - $sFile=__DIR__ . '/../../../config/inc_user2roles.php'; + + /** + * Load roles per user from config + * @return array + */ + private function _getUser2Roles(): array + { + $sFile = __DIR__ . '/../../../config/inc_user2roles.php'; return file_exists($sFile) ? require $sFile - : ['admin'=>['admin']] + : ['admin' => ['admin']] ; } + /** * TODO: reimplement * get the user groups of the current user from an internal source. * The function returns a flat aray with names of the groups * @return array */ - private function _getUserGroups(){ - $aGroups=array(); - if ($this->_sUsername){ - $aGroups[]="authenticated"; + private function _getUserGroups(): array + { + $aGroups = []; + if ($this->_sUsername) { + $aGroups[] = "authenticated"; // $aGroups[]='#'.$this->_sUsername; - $aUserDefinitions=$this->_getUser2Roles(); - foreach (array_keys($aUserDefinitions) as $sGroup){ - if (array_search($this->_sUsername, $aUserDefinitions[$sGroup])!==false){ - $aGroups[]=$sGroup; + $aUserDefinitions = $this->_getUser2Roles(); + foreach (array_keys($aUserDefinitions) as $sGroup) { + if (array_search($this->_sUsername, $aUserDefinitions[$sGroup]) !== false) { + $aGroups[] = $sGroup; } } } - $this->_aUserGroups=$aGroups; + $this->_aUserGroups = $aGroups; return $this->_aUserGroups; } @@ -114,66 +134,70 @@ class user { * The function returns a flat aray with names of the roles * @return array */ - private function _getUserPermission(){ - $aRoles=array(); - $aRolesDefinitions=require(__DIR__ . '/../../../config/inc_roles.php'); + private function _getUserPermission(): array + { + $aRoles = []; + $aRolesDefinitions = require(__DIR__ . '/../../../config/inc_roles.php'); // anonymous roles: - $aRoles=array_merge($aRoles, $aRolesDefinitions['all']); - - foreach (array_keys($aRolesDefinitions) as $sGroup){ - if ($this->hasGroup($sGroup)){ - $aRoles=array_merge($aRoles, $aRolesDefinitions[$sGroup]); + $aRoles = array_merge($aRoles, $aRolesDefinitions['all']); + + foreach (array_keys($aRolesDefinitions) as $sGroup) { + if ($this->hasGroup($sGroup)) { + $aRoles = array_merge($aRoles, $aRolesDefinitions[$sGroup]); } } - - $this->_aUserPermmissions= array_unique($aRoles); + + $this->_aUserPermmissions = array_unique($aRoles); return $this->_aUserPermmissions; } - - + + // ---------------------------------------------------------------------- // public ACTIONS // ---------------------------------------------------------------------- - - + + /** * authenticate a user with the configured methods + * * @global array $aConfig global config * @global array $aParams params (i.e. GET and POST) + * * @return boolean */ - public function authenticate(){ + public function authenticate(): bool + { global $aConfig, $aParams; - if(!isset($aConfig['auth']) || !is_array($aConfig['auth']) || !count($aConfig['auth']) || !isset($aParams['user'])){ + if (!isset($aConfig['auth']) || !is_array($aConfig['auth']) || !count($aConfig['auth']) || !isset($aParams['user'])) { return false; } - $sUser=$aParams['user']; - $sPassword=isset($aParams['password']) ? $aParams['password'] : false; + $sUser = $aParams['user']; + $sPassword = isset($aParams['password']) ? $aParams['password'] : false; - foreach (array_keys($aConfig['auth']) as $sAuthMethod){ - $oUserAuth=false; - switch ($sAuthMethod){ + foreach (array_keys($aConfig['auth']) as $sAuthMethod) { + $oUserAuth = false; + switch ($sAuthMethod) { case 'ldap': require_once("userauth.ldap.class.php"); - $oUserAuth=new userauthLdap($aConfig['auth']['ldap']); + $oUserAuth = new userauthLdap(); break; // implement other methods here // see userauth.ldap.class.php as simple example - + default: - echo 'WARNING: authmethod '.$sAuthMethod.' in your config is not implemented in '.basename(__FILE__).' and is useless so far.<br>'; + echo 'WARNING: authmethod ' . $sAuthMethod . ' in your config is not implemented in ' . basename(__FILE__) . ' and is useless so far.<br>'; } // if authentication fails then continue and try next method - if ($oUserAuth && $oUserAuth->authenticate($sUser, $sPassword)){ - + if ($oUserAuth && $oUserAuth->authenticate($sUser, $sPassword)) { + // set a session - it must correspondent with _autoDetectUser() // $_SESSION["PHP_AUTH_USER"]=$sUser; $this->setUser($sUser); return true; } // if authentication fails then continue and try next method - if(!$oUserAuth){ + if (!$oUserAuth) { echo "DEBUG: ERROR oUserAuth waasn't initialized for [$sAuthMethod].<br>"; } } @@ -184,98 +208,116 @@ class user { * logoff user * @return boolean */ - public function logoff(){ + public function logoff(): bool + { unset($_SESSION["PHP_AUTH_USER"]); $this->setUser(); return true; } - + /** * set an authenticated user and get its roles * @param string $sUser optional: set a given username + * @return void */ - public function setUser($sUser=false){ - if($sUser!==false){ - $this->_sUsername=$sUser; - $_SESSION["PHP_AUTH_USER"]=$sUser; + public function setUser(string $sUser = ''): void + { + if ($sUser) { + $this->_sUsername = $sUser; + $_SESSION["PHP_AUTH_USER"] = $sUser; } else { // check user from basic auth or cli - $this->_sUsername=$this->_autoDetectUser(); + $this->_sUsername = $this->_autoDetectUser(); } $this->_getUserGroups(); $this->_getUserPermission(); } - + /** - * return html code to display a denied message - * @return type + * Get html code to display a denied message + * @return string */ - public function showDenied(){ + public function showDenied(): string + { return '<div class="alert alert-danger" role="alert">' - . ($this->_sUsername - ? t("class-user-error-deny-no-role").'<br>'.$this->_sUsername.' --> ('.$this->_sLastCheckedPermission.')<br>' - : t("class-user-error-login-required") - ) - . '</div><br>' - . '<a href="/deployment/all/login/" class="btn btn-primary">'.t('menu-login').'</a>' + . ($this->_sUsername + ? t("class-user-error-deny-no-role") . '<br>' . $this->_sUsername . ' --> (' . $this->_sLastCheckedPermission . ')<br>' + : t("class-user-error-login-required") + ) + . '</div><br>' + . '<a href="/deployment/all/login/" class="btn btn-primary">' . t('menu-login') . '</a>' ; } - + // ---------------------------------------------------------------------- // public GETTER // ---------------------------------------------------------------------- - - // UNUSED SO FAR - public function getUser2Projects(){ + + /** + * UNUSED SO FAR + * Idea: limit user access to a set of projects + */ + public function getUser2Projects() + { return $this->_getUser2Projects(); } - - public function getUser2Roles(){ + + /** + * Get a list of all roles for the current user + * @return array + */ + public function getUser2Roles(): array + { return $this->_getUser2Roles(); } /** - * get the current username + * Get the current username * @return string */ - public function getUsername(){ + public function getUsername(): string + { return $this->_sUsername; } + /** - * get a flat array with roles of the current user + * Get a flat array with roles of the current user * @return array */ - public function getUserGroups(){ + public function getUserGroups(): array + { return $this->_aUserGroups; } /** - * get a flat array with roles of the current user + * Get a flat array with roles of the current user * @return array */ - public function getUserPermission(){ + public function getUserPermission(): array + { return $this->_aUserPermmissions; } /** * check if the current user has a given role name * @param string $sGroupname name of the role to check - * @return type + * @return bool */ - public function hasGroup($sGroupname){ - return (array_search($sGroupname, $this->_aUserGroups)!==false); + public function hasGroup($sGroupname) + { + return !!(array_search($sGroupname, $this->_aUserGroups) !== false); } /** * check if the current user has a given role name * @param string $sPermission name of the role to check - * @return type + * @return boolean */ - public function hasPermission($sPermission){ - $this->_sLastCheckedPermission=$sPermission; - $bReturn=array_search($sPermission, $this->_aUserPermmissions)!==false; + public function hasPermission($sPermission): bool + { + $this->_sLastCheckedPermission = $sPermission; + $bReturn = !!(array_search($sPermission, $this->_aUserPermmissions) !== false); // $this->log(__FUNCTION__ . "($sRolename) -> " . $bReturn ? 'true' : 'false'); return $bReturn; } - } diff --git a/public_html/deployment/classes/userauth.interface.php b/public_html/deployment/classes/userauth.interface.php index 83a4a3acb9c1e5dab5938aa4ea8a39b66a05a19c..3bd5c8372f6e6e2ad1e560629ab5f13c7e170826 100644 --- a/public_html/deployment/classes/userauth.interface.php +++ b/public_html/deployment/classes/userauth.interface.php @@ -1,14 +1,23 @@ <?php /** * interface for user authentication + * * @author axel.hahn@iml.unibe.ch + * + * Axel <axel.hahn@unibe.ch> + * 2024-08-29 Axel php8 only; added variable types; use short array syntax */ -interface iUserAuth { +interface iUserAuth +{ /** - * verify if a given user and password combination is correct + * Verify if a given user and password combination is correct + * + * @param string $sUser username + * @param string $sPassword password + * @return boolean */ - public function authenticate($sUser, $sPassword); - + public function authenticate(string $sUser, string $sPassword): bool; + } \ No newline at end of file diff --git a/public_html/deployment/classes/userauth.ldap.class.php b/public_html/deployment/classes/userauth.ldap.class.php index 07c440ad5dcdba27fd6b7fe1548f018b78da33f2..a8e5bc1cc88d1e0b11b0db02df591249b3debdb4 100644 --- a/public_html/deployment/classes/userauth.ldap.class.php +++ b/public_html/deployment/classes/userauth.ldap.class.php @@ -8,42 +8,58 @@ require_once("ldap.class.php"); * implements userauth interface * * @author hahn + * + * Axel <axel.hahn@unibe.ch> + * 2024-08-29 Axel php8 only; added variable types */ -class userauthLdap implements iUserAuth { +class userauthLdap implements iUserAuth +{ /** * object for ldap actions * @var object */ - private $_oLdap=false; - + private object $_oLdap; + // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- - public function __construct() { + + /** + * Constructor + */ + public function __construct() + { global $aConfig; - $this->_oLdap=new imlldap($aConfig['auth']['ldap']); - + $this->_oLdap = new imlldap($aConfig['auth']['ldap']); + // first test of ldap connection // $this->_oLdap->debugOn(); $this->_oLdap->connect(); - return true; } - - public function __destruct() { + + /** + * Destructor + * Close ldap connection + */ + public function __destruct() + { $this->_oLdap->close(); } - + // ---------------------------------------------------------------------- // implementation // ---------------------------------------------------------------------- + /** - * verify if a given user and password combination is correct - * @param string $sUser username - * @param password $sPassword password + * Verify if a given user and password combination is correct + * + * @param string $sUser username + * @param string $sPassword password * @return boolean */ - public function authenticate($sUser, $sPassword){ + public function authenticate(string $sUser, string $sPassword): bool + { return $this->_oLdap->verifyPassword($sUser, $sPassword); } diff --git a/public_html/deployment/classes/vcs.git.class.php b/public_html/deployment/classes/vcs.git.class.php index 312f957b56900a16797853c5d064004cfe780ed9..dc16c3dd676f85c7aa091e12171eb26ab23eabc9 100644 --- a/public_html/deployment/classes/vcs.git.class.php +++ b/public_html/deployment/classes/vcs.git.class.php @@ -1,7 +1,7 @@ <?php require_once("vcs.interface.php"); -require_once __DIR__.'/../../vendor/axelhahn/ahcache/cache.class.php'; +require_once __DIR__ . '/../../vendor/axelhahn/ahcache/cache.class.php'; /** * version control system :: GIT @@ -9,77 +9,88 @@ require_once __DIR__.'/../../vendor/axelhahn/ahcache/cache.class.php'; * * * @author hahn + * + * Axel: <axel.hahn@unibe.ch> + * (...) + * 2024-08-28 Axel php8 only; added variable types; short array syntax + */ -class vcs implements iVcs { -// class vcs { +class vcs implements iVcs +{ + // class vcs { /** * configuration * @var array */ - private $_aCfg = array(); + private array $_aCfg = []; /** * temp dir to fetch repo version and ommit message; its value will be * generated in set_config() * @var string */ - private $_sTempDir = false; // + private string $_sTempDir = ''; // /** * filename of ssh key file with complete path * @var string */ - private $_sKeyfile = false; + private string $_sKeyfile = ''; /** * filename of ssh wrapper script with complete path * @var string */ - private $_sWrapper = false; + private string $_sWrapper = ''; /** * flat array with remote branch names * @var array */ - private $_aRemoteBranches = array(); + private array $_aRemoteBranches = []; /** * name of the default remote branch to access * @var string */ - private $_sCurrentBranch = ""; + private string $_sCurrentBranch = ''; /** - * constructor + * Constructor * @param array $aRepoConfig */ - public function __construct($aRepoConfig = array()) { + public function __construct(array $aRepoConfig = []) + { $this->setConfig($aRepoConfig); $this->getRemoteBranches(); // to fill the cache } - + /** - * add a log messsage + * Add a log messsage * @global object $oLog + * * @param string $sMessage messeage text * @param string $sLevel warnlevel of the given message * @return bool */ - private function log($sMessage,$sLevel="info"){ + private function log(string $sMessage, string $sLevel = "info"): bool + { global $oCLog; - return $oCLog->add(basename(__FILE__)." class ".__CLASS__." - ".$sMessage,$sLevel); + return $oCLog->add(basename(__FILE__) . " class " . __CLASS__ . " - " . $sMessage, $sLevel); } /** - * set a config and update internal (private) variables + * Set a config and update internal (private) variables + * * @param array $aRepoConfig * @return boolean */ - public function setConfig($aRepoConfig = array()) { + public function setConfig(array $aRepoConfig = []): bool + { // checks - // foreach (array("type", "url") as $key) { - foreach (array("type") as $key) { + // foreach (["type", "url"] as $key) { + foreach (["type"] as $key) { if (!isset($aRepoConfig[$key])) { die("ERROR: key $key does not exist in config <pre>" . print_r($aRepoConfig, true) . "</pre>"); } @@ -99,50 +110,56 @@ class vcs implements iVcs { $this->_sWrapper = $this->_aCfg["appRootDir"] . "/shellscripts/gitsshwrapper.sh"; $this->_setTempdir(); - return $this->_aCfg = $aRepoConfig; + $this->_aCfg = $aRepoConfig; + return true; } /** - * set directory für current branch of a project below tempdir - * In it the branch will be initialized - * @return type + * Get directory für current branch of a project below tempdir + * If it does not exist yet it will be created and the repository will be initialized. + * + * @return string */ - private function _setTempdir() { + private function _setTempdir(): string + { $this->_sTempDir = $this->_aCfg["url"]; $this->_sTempDir = preg_replace('/[\@\.\:\/]/', '_', $this->_sTempDir); $this->_sTempDir = $this->_aCfg["tmpDir"] . '/checkout_vcsgit_' . $this->_sTempDir . '/'; - $this->_sTempDir .= preg_replace('/[\@\.\:\/]/', '_', ($this->_sCurrentBranch ? $this->_sCurrentBranch : '__no-branch__') ) . '/'; + $this->_sTempDir .= preg_replace('/[\@\.\:\/]/', '_', ($this->_sCurrentBranch ? $this->_sCurrentBranch : '__no-branch__')) . '/'; - if (!is_dir($this->_sTempDir . ".git") ) { - $this->log(__FUNCTION__." does not exist yet: " . $this->_sTempDir . ".git"); + if (!is_dir($this->_sTempDir . ".git")) { + $this->log(__FUNCTION__ . " does not exist yet: " . $this->_sTempDir . ".git"); $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; '; - $sGitCmd.='mkdir -p "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && '; - $sGitCmd.='git init >/dev/null && '; - $sGitCmd.='git remote add origin "' . $this->getUrl() . '" 2>&1 '; + $sGitCmd .= 'mkdir -p "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && '; + $sGitCmd .= 'git init >/dev/null && '; + $sGitCmd .= 'git remote add origin "' . $this->getUrl() . '" 2>&1 '; // $sGitCmd='time ('.$sGitCmd.')'; // exec($sGitCmd, $aOutput, $iRc); - $this->log(__FUNCTION__." start command <code>$sGitCmd</code>"); - exec($sGitCmd, $aOutput,$iRc); - $this->log(__FUNCTION__." command ended with rc=$iRc ". '<pre>'.implode("\n", $aOutput).'</pre>', ($iRc==0 ? 'info':'error')); + $this->log(__FUNCTION__ . " start command <code>$sGitCmd</code>"); + exec($sGitCmd, $aOutput, $iRc); + $this->log(__FUNCTION__ . " command ended with rc=$iRc " . '<pre>' . implode("\n", $aOutput) . '</pre>', ($iRc == 0 ? 'info' : 'error')); } return $this->_sTempDir; } /** - * set the current branch + * Set the current branch + * * @param string $sBranchname name of the branch + * @return void */ - public function setCurrentBranch($sBranchname) { + public function setCurrentBranch($sBranchname): void + { $this->_sCurrentBranch = $sBranchname; $this->_setTempdir(); - return $this->_sCurrentBranch; } /** * helper: dump values * @return boolean */ - public function dump() { + public function dump(): bool + { echo "<h3>Dump class " . __CLASS__ . "</h3>"; echo "config array: <pre>" . print_r($this->_aCfg, true) . "</pre>"; echo "temp dir to read revision: " . $this->_sTempDir . "<br>"; @@ -151,10 +168,14 @@ class vcs implements iVcs { /** * cleanup unneeded files and directories in a checked out directory - * and remove all vcs specific files and directories + * and remove all vcs specific files and directories. + * This method works on linux only + * + * @param string $sWorkDir path of the build directory to cleanup the git meta data from * @return bool */ - public function cleanupWorkdir($sWorkDir) { + public function cleanupWorkdir(string $sWorkDir): bool + { if (!is_dir($sWorkDir)) { return false; } @@ -164,32 +185,36 @@ class vcs implements iVcs { } /** - * get the current branch + * Get the current branch + * * @param bool $bReturnMasterIfEmpty flag: if there is no current branch then detect a master branch * @return string */ - public function getCurrentBranch($bReturnMasterIfEmpty=false) { - if(!$this->_sCurrentBranch){ - if ($bReturnMasterIfEmpty){ - $this->_sCurrentBranch=$this->_getMasterbranchname(); + public function getCurrentBranch(bool $bReturnMasterIfEmpty = false): string + { + if (!$this->_sCurrentBranch) { + if ($bReturnMasterIfEmpty) { + $this->_sCurrentBranch = $this->_getMasterbranchname(); } } return $this->_sCurrentBranch; } /** - * detect an existing master branch ... and return one of 'origin/main' | 'origin/master' + * Detect an existing master branch ... and return one of 'origin/main' | 'origin/master' + * * @return string */ - protected function _getMasterbranchname(){ - $sMasterBranch=''; - $aMasternames=['origin/main', 'origin/master']; - - $aAllBranches=$this->getRemoteBranches(); - if(count($aAllBranches)){ - foreach($aMasternames as $sBranchToTest){ - if (isset($aAllBranches[$sBranchToTest])){ - $sMasterBranch=$sBranchToTest; + protected function _getMasterbranchname(): string + { + $sMasterBranch = ''; + $aMasternames = ['origin/main', 'origin/master']; + + $aAllBranches = $this->getRemoteBranches(); + if (count($aAllBranches)) { + foreach ($aMasternames as $sBranchToTest) { + if (isset($aAllBranches[$sBranchToTest])) { + $sMasterBranch = $sBranchToTest; break; } } @@ -198,98 +223,109 @@ class vcs implements iVcs { } /** - * return the build type, i.e. git|svn|cvs| + * Get the build type, i.e. git|svn|cvs| * @return string */ - public function getBuildType() { - return $this->_aCfg["type"]; + public function getBuildType(): string + { + return $this->_aCfg["type"] ?? ''; } /** - * get a nice name for a cache module based on repo url - * @return type + * Get a nice name for a cache module based on repo url + * + * @return string */ - private function _getNameOfCacheModule() { + private function _getNameOfCacheModule(): string + { return preg_replace('/([^0-9a-z])/i', "", $this->getUrl()); } /** - * cleanup cache data for this project (revisions, list of branches+tags) + * Cleanup cache data for this project (revisions, list of branches+tags) + * + * @param int $iAge max age in sec; older items will be deleted * @return bool */ - public function cleanupCache($iAge) { - $this->log(__FUNCTION__." start"); + public function cleanupCache(int $iAge): string + { + $this->log(__FUNCTION__ . " start"); $oCache = new AhCache($this->_getNameOfCacheModule()); ob_start(); - $oCache->cleanup((int)$iAge); + $oCache->cleanup((int) $iAge); $sOut = ob_get_contents(); ob_end_clean(); return $sOut; } /** - * helper: cache hash with all branches + * helper: store hash with all branches in cache * It saves 1.5 sec for reading 300 branches + * * @return boolean */ - private function _cacheRemoteBranches() { - $iTtl=300; + private function _cacheRemoteBranches(): bool + { + $iTtl = 300; $oCache = new AhCache($this->_getNameOfCacheModule(), "RemoteBranches"); // $this->log(__FUNCTION__." <pre>".print_r($this->_aRemoteBranches, 1)."</pre>"); $oCache->write($this->_aRemoteBranches, $iTtl); return true; } - + /** - * read remote repository and get an array with names and revisions of + * Read remote repository and get an array with names and revisions of * all branches and tags - * pre branch you get an array element with the keys revision, name, type + * per branch you get an array element with the keys revision, name, type + * It returns false if there is no git url + * * @param bool $bIgnoreCache flag to overrde cache * @return array */ - private function _fetchRemoteBranches($bIgnoreCache = false) { - $this->log(__FUNCTION__."(bIgnoreCache = ".($bIgnoreCache ? 'true' : 'false').") start"); - $aReturn = array(); + private function _fetchRemoteBranches($bIgnoreCache = false): bool|array + { + $this->log(__FUNCTION__ . "(bIgnoreCache = " . ($bIgnoreCache ? 'true' : 'false') . ") start"); + $aReturn = []; - $sGitUrl=$this->getUrl(); + $sGitUrl = $this->getUrl(); if (!$sGitUrl) { return false; } $oCache = new AhCache($this->_getNameOfCacheModule(), "RemoteBranches"); - $aOutput=[]; - $iRc=false; + $aOutput = []; + $iRc = false; // list of cached branch keys if ($oCache->isExpired() || $bIgnoreCache) { // workdir is on level of set project ... going 1 level up means to leave the dir with the current branch $sWorkdir = dirname($this->_sTempDir) . '/fetchRemoteBranches/'; - $this->log(__FUNCTION__." - sWorkdir = $sWorkdir"); + $this->log(__FUNCTION__ . " - sWorkdir = $sWorkdir"); $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; '; - + if (is_dir($sWorkdir . ".git")) { // if a subdir .git exists: // Verify if git remote -v contains the current git url // If not, we delete it - $sPreCmd='cd "' . $sWorkdir . '" 2>&1 && git remote -v 2>&1 | grep -F "' . $sGitUrl . '" >/dev/null || ( echo "DELETING .git dir..."; rm -rf .git && rc=$?; echo "rc=$rc"; sleep 1; exit $rc) '; - $this->log(__FUNCTION__." - start PRE command <code>$sPreCmd</code>"); + $sPreCmd = 'cd "' . $sWorkdir . '" 2>&1 && git remote -v 2>&1 | grep -F "' . $sGitUrl . '" >/dev/null || ( echo "DELETING .git dir..."; rm -rf .git && rc=$?; echo "rc=$rc"; sleep 1; exit $rc) '; + $this->log(__FUNCTION__ . " - start PRE command <code>$sPreCmd</code>"); exec($sPreCmd, $aPreLines, $iRc); - if (!$iRc==0){ - $this->log(__FUNCTION__." <code>".print_r($aPreLines, 1)."</code> rc=$iRc"); + if (!$iRc == 0) { + $this->log(__FUNCTION__ . " <code>" . print_r($aPreLines, 1) . "</code> rc=$iRc"); } } if (!is_dir($sWorkdir . ".git")) { - $sGitCmd.='mkdir -p "' . $sWorkdir . '" && cd "' . $sWorkdir . '" && '; - $sGitCmd.='git init >/dev/null && '; - $sGitCmd.='git remote add origin "' . $sGitUrl . '" 2>&1 && '; + $sGitCmd .= 'mkdir -p "' . $sWorkdir . '" && cd "' . $sWorkdir . '" && '; + $sGitCmd .= 'git init >/dev/null && '; + $sGitCmd .= 'git remote add origin "' . $sGitUrl . '" 2>&1 && '; } else { - $sGitCmd.='cd "' . $sWorkdir . '" 2>&1 && '; + $sGitCmd .= 'cd "' . $sWorkdir . '" 2>&1 && '; } - $sGitCmd.='git ls-remote --heads --tags origin 2>&1 ;'; - $this->log(__FUNCTION__." - start command <code>$sGitCmd</code>"); + $sGitCmd .= 'git ls-remote --heads --tags origin 2>&1 ;'; + $this->log(__FUNCTION__ . " - start command <code>$sGitCmd</code>"); exec($sGitCmd, $aOutput, $iRc); - $this->log(__FUNCTION__." - command ended with rc=$iRc ". '<pre>'.implode("\n", $aOutput).'</pre>', ($iRc==0 ? 'info':'error')); + $this->log(__FUNCTION__ . " - command ended with rc=$iRc " . '<pre>' . implode("\n", $aOutput) . '</pre>', ($iRc == 0 ? 'info' : 'error')); if ($iRc == 0) { $this->log(__FUNCTION__ . ' start reading all branches'); @@ -314,14 +350,14 @@ class vcs implements iVcs { // http://stackoverflow.com/questions/15472107/when-listing-git-ls-remote-why-theres-after-the-tag-name if (!preg_match('/\^\{\}$/', $sBranch)) { $sRevision = $aTmp[0]; - $sType = preg_replace('#/.*$#', '', $sBranchPath); + $sType = preg_replace('#/.*$#', '', $sBranchPath); $sName = ($sType == "heads") ? "origin/" . $sBranch : $sBranch; - + $sBranchKey = $sName; // $this->log(__FUNCTION__ . ' $sBranchKey = '.$sBranchKey); // $sMessage = $this->getCommitmessageByBranch($sName, $sRevision); - $aReturn[$sBranchKey] = array( + $aReturn[$sBranchKey] = [ // 'debug'=> $aTmp, 'revision' => $sRevision, 'name' => $sName, @@ -329,15 +365,15 @@ class vcs implements iVcs { 'label' => $sType . ': ' . $sBranch, 'type' => $sType, // 'message' => $sMessage - ); + ]; } } $this->_aRemoteBranches = $aReturn; - $this->log(__FUNCTION__ . ' '.count($aReturn).' branches: <pre>'.print_r($this->_aRemoteBranches, 1).'</pre>'); + $this->log(__FUNCTION__ . ' ' . count($aReturn) . ' branches: <pre>' . print_r($this->_aRemoteBranches, 1) . '</pre>'); $this->_cacheRemoteBranches(); } else { // $this->_aRemoteBranches = $oCache->read(); - $this->log(__FUNCTION__." - No git access? --> deleting cache of former fetched branches..."); + $this->log(__FUNCTION__ . " - No git access? --> deleting cache of former fetched branches..."); $oCache->delete(); $this->_aRemoteBranches = []; } @@ -349,42 +385,46 @@ class vcs implements iVcs { } /** - * get a flat array with names of all remote branches + * Get a flat array with names of all remote branches * @param bool $bIgnoreCache flag: ignore caching; default: use cache * @return array */ - public function getRemoteBranches($bIgnoreCache=false) { - $this->log(__FUNCTION__."($bIgnoreCache) start"); + public function getRemoteBranches(bool $bIgnoreCache = false): array + { + $this->log(__FUNCTION__ . "($bIgnoreCache) start"); if (!$this->_aRemoteBranches || $bIgnoreCache) { - $this->log(__FUNCTION__."($bIgnoreCache) --> fetching fresh data"); + $this->log(__FUNCTION__ . "($bIgnoreCache) --> fetching fresh data"); $this->_fetchRemoteBranches($bIgnoreCache); } else { - $this->log(__FUNCTION__."($bIgnoreCache) --> returning cached data"); + $this->log(__FUNCTION__ . "($bIgnoreCache) --> returning cached data"); } return $this->_aRemoteBranches; } /** - * get current revision and commit message from remote repository + * Get current revision and commit message from remote repository * @see $this::getRevision + * It returns false if no branch is set + * * @param boolean $bRefresh optional: refresh data; default: use cache - * @return array + * @return bool|array */ - public function getRepoRevision($bRefresh=false) { - $this->log(__FUNCTION__."($bRefresh) start"); - if(!$this->_sCurrentBranch){ + public function getRepoRevision(bool $bRefresh = false): bool|array + { + $this->log(__FUNCTION__ . "($bRefresh) start"); + if (!$this->_sCurrentBranch) { return false; } $sMessage = $this->getCommitmessageByBranch(false, $bRefresh ? 'dummy_to_force_refresh' : false); if ($sMessage) { - $aReturn = array( + $aReturn = [ 'branch' => $this->_sCurrentBranch, 'shortname' => $this->_aRemoteBranches[$this->_sCurrentBranch]['shortname'], 'revision' => $this->_aRemoteBranches[$this->_sCurrentBranch]['revision'], 'type' => $this->_aRemoteBranches[$this->_sCurrentBranch]['type'], 'message' => $sMessage, - '_data'=>$this->_aRemoteBranches[$this->_sCurrentBranch], - ); + '_data' => $this->_aRemoteBranches[$this->_sCurrentBranch], + ]; } else { $aReturn = $this->getRevision(false); } @@ -392,31 +432,35 @@ class vcs implements iVcs { } /** - * get a commit message of a given branch + * Get a commit message of a given branch. + * It reurns if no branch was found in meta infos + * * @param string $sBranch name of a branch * @param string $sVerifyRevision optional: revision to verify if it is the newsest * @return string */ - public function getCommitmessageByBranch($sBranch = false, $sVerifyRevision = false) { - $this->log(__FUNCTION__."($sBranch, $sVerifyRevision) start"); + public function getCommitmessageByBranch(string $sBranch = '', string $sVerifyRevision = ''): bool|string + { + $this->log(__FUNCTION__ . "($sBranch, $sVerifyRevision) start"); if (!$sBranch) { $sBranch = $this->_sCurrentBranch; } // try to get infos from the cache if ( - is_array($this->_aRemoteBranches) - && ( - isset($this->_aRemoteBranches[$sBranch]) && $sVerifyRevision && $this->_aRemoteBranches[$sBranch]['revision'] == $sVerifyRevision - || - isset($this->_aRemoteBranches[$sBranch]) && !$sVerifyRevision - ) + is_array($this->_aRemoteBranches) + && ( + isset($this->_aRemoteBranches[$sBranch]) && $sVerifyRevision && $this->_aRemoteBranches[$sBranch]['revision'] == $sVerifyRevision + || + isset($this->_aRemoteBranches[$sBranch]) && !$sVerifyRevision + ) + && isset($this->_aRemoteBranches[$sBranch]['message']) ) { // it is up to date - doing nothing - $this->log(__FUNCTION__." return cached data"); + $this->log(__FUNCTION__ . " return cached data"); return $this->_aRemoteBranches[$sBranch]['message']; } // ok, then I need to read it - $this->log(__FUNCTION__." return fresh data"); + $this->log(__FUNCTION__ . " return fresh data"); if ($this->_sCurrentBranch != $sBranch) { $sSaveBranch = $this->_sCurrentBranch; $this->setCurrentBranch($sBranch); @@ -425,14 +469,14 @@ class vcs implements iVcs { } else { $a = $this->getRevision(false); } - if(!isset($a['branch'])){ + if (!isset($a['branch'])) { return false; } // merge with cached info ... to add type and label - if(isset($this->_aRemoteBranches[$a['branch']])){ - $this->_aRemoteBranches[$a['branch']]=array_merge($this->_aRemoteBranches[$a['branch']], $a); + if (isset($this->_aRemoteBranches[$a['branch']])) { + $this->_aRemoteBranches[$a['branch']] = array_merge($this->_aRemoteBranches[$a['branch']], $a); } else { - $this->_aRemoteBranches[$a['branch']]=$a; + $this->_aRemoteBranches[$a['branch']] = $a; } // store in cache $this->_cacheRemoteBranches(); @@ -440,29 +484,31 @@ class vcs implements iVcs { } /** - * get current revision and commit message from an existing directory or a + * Get current revision and commit message from an existing directory or a * remote repository * the return will fill $this->_aData["phases"]["source"] in project class * (array keys are revision, message or error) * if ok: - * array( + * [ * "branch" => $sRevision, * "revision" => $sRevision, * "message" => $sCommitmessage - * ); + * ]; * * on error: - * array( + * [ * "error" => $sErrormessage, - * ); + * ]; + * * @param string $sWorkDir optional: local directory with initialized git repo - * @return array + * @return bool|array */ - public function getRevision($sWorkDir = false) { - $this->log(__FUNCTION__." start"); - $aReturn = array(); - $aOutput=array(); - $iRc=false; + public function getRevision(string $sWorkDir = ''): bool|array + { + $this->log(__FUNCTION__ . " start"); + $aReturn = []; + $aOutput = []; + $iRc = false; if (!$this->getUrl()) { return false; } @@ -477,33 +523,33 @@ class vcs implements iVcs { $sGitCmd.='git clone -b '.$this->_sCurrentBranch.' --single-branch '.$this->getUrl().' --depth 1 --bare "' . $sWorkDir . '" 2>&1; rm -rf "' . $sWorkDir . '"'; */ if ($sWorkDir) { - $sGitCmd.='cd "' . $sWorkDir . '" && '; + $sGitCmd .= 'cd "' . $sWorkDir . '" && '; } else { if (!file_exists($this->_sTempDir . ".git")) { - $this->log(__FUNCTION__." does not exist yet: ".$this->_sTempDir . ".git"); - $sGitCmd.='mkdir -p "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && '; - $sGitCmd.='git init >/dev/null 2>&1 && '; - $sGitCmd.='git remote add origin "' . $this->getUrl() . '" 2>&1 && '; + $this->log(__FUNCTION__ . " does not exist yet: " . $this->_sTempDir . ".git"); + $sGitCmd .= 'mkdir -p "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && '; + $sGitCmd .= 'git init >/dev/null 2>&1 && '; + $sGitCmd .= 'git remote add origin "' . $this->getUrl() . '" 2>&1 && '; } else { - $sGitCmd.='cd "' . $this->_sTempDir . '" && '; + $sGitCmd .= 'cd "' . $this->_sTempDir . '" && '; } // TODO: git 1.9 does needs only the line with --tags - $sGitCmd.=' ( ' - // . 'git fetch --update-head-ok --tags --depth 1 2>&1 ; ' // 1.5 s - . 'git fetch --update-head-ok --tags --depth 1 2>&1 ; ' // 1.5 s - //. 'git fetch --update-head-ok --depth 1 2>&1 ' // 1.5 s - . ') && '; + $sGitCmd .= ' ( ' + // . 'git fetch --update-head-ok --tags --depth 1 2>&1 ; ' // 1.5 s + . 'git fetch --update-head-ok --tags --depth 1 2>&1 ; ' // 1.5 s + //. 'git fetch --update-head-ok --depth 1 2>&1 ' // 1.5 s + . ') && '; } - $sGitCmd.='git log -1 "' . $this->_sCurrentBranch . '" 2>&1 ; '; // 0.0 s + $sGitCmd .= 'git log -1 "' . $this->_sCurrentBranch . '" 2>&1 ; '; // 0.0 s // $sGitCmd.='git log -1 2>&1 ; '; // 0.0 s - $this->log(__FUNCTION__." start command <code>$sGitCmd</code>"); + $this->log(__FUNCTION__ . " start command <code>$sGitCmd</code>"); // $sLoginfo = shell_exec($sGitCmd); exec($sGitCmd, $aOutput, $iRc); - $this->log(__FUNCTION__." command ended with rc=$iRc ". '<pre>'.implode("\n", $aOutput).'</pre>', ($iRc==0 ? 'info':'error')); - $sLoginfo= implode("\n", $aOutput); - + $this->log(__FUNCTION__ . " command ended with rc=$iRc " . '<pre>' . implode("\n", $aOutput) . '</pre>', ($iRc == 0 ? 'info' : 'error')); + $sLoginfo = implode("\n", $aOutput); + /* * * example output: @@ -521,7 +567,7 @@ class vcs implements iVcs { // parse revision $sRevision = false; - $aRev=array(); + $aRev = []; if (preg_match('#commit\ (.*)#', $sLoginfo, $aRev)) { $sRevision = $aRev[1]; } @@ -536,30 +582,35 @@ class vcs implements iVcs { // $sCommitMsg=preg_replace('/Author:\ .*\n/', '', $sCommitMsg); // $sCommitMsg=preg_replace('/Date:\ .*\n/', '', $sCommitMsg); - $aReturn = array( + $aReturn = [ "branch" => $this->_sCurrentBranch, "revision" => $sRevision, "message" => $sCommitMsg // ."\n". microtime(true), - ); + ]; } else { if (!$sLoginfo) { $sLoginfo = $sGitCmd; } // echo "DEBUG: error on reading git revision<br>"; - $aReturn = array( + $aReturn = [ "error" => '<pre>' . $sLoginfo . '<hr>' . $sGitCmd . '</pre>' - ); + ]; } // $this->log(__FUNCTION__ . ' return is <pre>'.print_r($aReturn, 1).'</pre>'); return $aReturn; } /** - * get sources from vsc and check them out in given directory - * @return bool + * Get sources from vsc and check them out in given directory + * It returns false if the workdir was not found or the url for git repo is not set + * Otherwise it retunrs the output of git clone + git checkout + * + * @param string $sWorkDir working dir where to check out source url + * @return bool|string */ - public function getSources($sWorkDir) { - $this->log(__FUNCTION__." start"); + public function getSources(string $sWorkDir): bool|string + { + $this->log(__FUNCTION__ . " start"); if (!$sWorkDir || !is_dir($sWorkDir)) { return false; } @@ -569,36 +620,42 @@ class vcs implements iVcs { $sBranchname = str_replace("origin/", "", $this->_sCurrentBranch); $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; '; - $aOutput=[]; - $iRc=false; - + $aOutput = []; + $iRc = false; + // this does not checkout tags in git v1.7 - only branches: // $sGitCmd .= 'echo git clone --depth 1 --recursive --branch "' . $sBranchname . '" "' . $this->getUrl() . '" "' . $sWorkDir . '" ; '; // $sGitCmd .= ' git clone --depth 1 --recursive --branch "' . $sBranchname . '" "' . $this->getUrl() . '" "' . $sWorkDir . '" 2>&1; '; // - $sGitCmd .= 'echo git clone "' . $this->getUrl() . '" "'.$sWorkDir.'" 2>&1 \&\& cd "'.$sWorkDir.'" \&\& git checkout "' . $sBranchname . '" ; '; - $sGitCmd .= ' git clone "' . $this->getUrl() . '" "'.$sWorkDir.'" 2>&1 && cd "'.$sWorkDir.'" && git checkout "' . $sBranchname . '" 2>&1 '; - $this->log(__FUNCTION__." start command <code>$sGitCmd</code>"); + $sGitCmd .= 'echo git clone "' . $this->getUrl() . '" "' . $sWorkDir . '" 2>&1 \&\& cd "' . $sWorkDir . '" \&\& git checkout "' . $sBranchname . '" ; '; + $sGitCmd .= ' git clone "' . $this->getUrl() . '" "' . $sWorkDir . '" 2>&1 && cd "' . $sWorkDir . '" && git checkout "' . $sBranchname . '" 2>&1 '; + $this->log(__FUNCTION__ . " start command <code>$sGitCmd</code>"); // $sReturn = shell_exec($sGitCmd); exec($sGitCmd, $aOutput, $iRc); - $this->log(__FUNCTION__." command ended with rc=$iRc ". '<pre>'.implode("\n", $aOutput).'</pre>', ($iRc==0 ? 'info':'error')); - return implode("\n", $aOutput). "\nrc=$iRc"; + $this->log(__FUNCTION__ . " command ended with rc=$iRc " . '<pre>' . implode("\n", $aOutput) . '</pre>', ($iRc == 0 ? 'info' : 'error')); + return implode("\n", $aOutput) . "\nrc=$iRc"; } /** - * return url to vcs sources + * Get url to vcs sources + * + * @return string */ - public function getUrl() { - $this->log(__FUNCTION__." --> ".$this->_aCfg["url"]); - return $this->_aCfg["url"]; + public function getUrl(): string + { + $this->log(__FUNCTION__ . " --> " . $this->_aCfg["url"]); + return $this->_aCfg["url"] ?? ''; } /** - * return url to view sources in webrowser to generate an infolink + * Get url to view sources in webrowser to generate an infolink + * + * @return string */ - public function getWebGuiUrl() { - $this->log(__FUNCTION__." start"); - return $this->_aCfg["webaccess"]; + public function getWebGuiUrl(): string + { + $this->log(__FUNCTION__ . " start"); + return $this->_aCfg["webaccess"] ?? ''; } } diff --git a/public_html/deployment/classes/vcs.interface.php b/public_html/deployment/classes/vcs.interface.php index 4ced62baa3d2120643dc6cc981830310cedd30ed..d52a50802460ec613a87da5c0b9177493e9f0aa3 100644 --- a/public_html/deployment/classes/vcs.interface.php +++ b/public_html/deployment/classes/vcs.interface.php @@ -4,6 +4,10 @@ * * interface for a version control system * @author hahn + * + * Axel: <axel.hahn@unibe.ch> + * (...) + * 2024-08-28 Axel php8 only; added variable types; short array syntax */ interface iVcs { @@ -14,46 +18,54 @@ interface iVcs { /** * return url to vcs sources */ - public function getUrl(); + public function getUrl(): string; /** * return url to view sources in webrowser to generate an infolink */ - public function getWebGuiUrl(); + public function getWebGuiUrl(): string; /** * return the build type, i.e. git|svn|cvs| */ - public function getBuildType(); + public function getBuildType(): string; // ---------------------------------------------------------------------- // actions // ---------------------------------------------------------------------- + /** * cleanup unneeded files and directories in a checked out directory - * and remove all vcs specific files and directories + * and remove all vcs specific files and directories. + * This method works on linux only + * + * @param string $sWorkDir path of the build directory to cleanup the vcs meta data from * @return bool */ - public function cleanupWorkdir($sWorkDir); + public function cleanupWorkdir(string $sWorkDir): bool; /** * get current revision and commit message from remote repository * @param boolean $bRefresh optional: refresh data; default: use cache * @return array */ - public function getRepoRevision($bRefresh=false); + public function getRepoRevision(bool $bRefresh=false); /** * get current revision and log message from given directory - * @return array + * @return bool|array */ - public function getRevision($sWorkDir); - + public function getRevision(string $sWorkDir = ''): bool|array; + /** - * get sources from vsc and check them out in given directory - * @return bool + * Get sources from vsc and check them out in given directory + * It returns false if the workdir was not found or the url for git repo is not set + * Otherwise it retunrs the output of the checkout commands + * + * @param string $sWorkDir working dir where to check out source url + * @return bool|string */ - public function getSources($sWorkDir); + public function getSources(string $sWorkDir): bool|string; } diff --git a/public_html/deployment/inc_functions.php b/public_html/deployment/inc_functions.php index 57be339e5815a738b33b927fbea28822141955cc..e68f01769be567ed7739b5a43b033b0ca1791765 100644 --- a/public_html/deployment/inc_functions.php +++ b/public_html/deployment/inc_functions.php @@ -8,10 +8,12 @@ --------------------------------------------------------------------- 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ global $aParams; -$aParams = array(); +$aParams = []; // remark: $_SERVER does not exist in CLI @@ -49,7 +51,7 @@ if (isset($_SERVER) && is_array($_SERVER) && array_key_exists("REQUEST_URI", $_S $aParams[$key] = $value; /* force integer params - foreach (array("id") as $sKey) { + foreach (["id"] as $sKey) { if (array_key_exists($sKey, $aParams)) { $aParams[$sKey]=(int)$aParams[$sKey]; } @@ -58,34 +60,45 @@ if (isset($_SERVER) && is_array($_SERVER) && array_key_exists("REQUEST_URI", $_S foreach (array_keys($aParams) as $sKey) { $aParams[$sKey] = is_string($aParams[$sKey]) - ? str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $aParams[$sKey]) + ? str_replace(['\\', "\0", "\n", "\r", "'", '"', "\x1a"], ['\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'], $aParams[$sKey]) : $aParams[$sKey]; } } /** - * get home link as button + * Get home link as button + * + * @global object $oHtml + * + * @param string $sClass css class for the button; default: "btn btn-default" * @return string */ -function aHome($sClass = "btn btn-default") +function aHome(string $sClass = "btn btn-default"): string { global $oHtml; // if (!array_key_exists("prj", $aParams)) return false; - return $oHtml->getLinkButton(array( + return $oHtml->getLinkButton([ 'href' => '/deployment/?', 'icon' => 'overview', 'class' => $sClass, 'label' => t("menu-overview"), - )); + ]); } /** - * get project Home link as button - * @return string + * Get project Home link as button. + * It returns false if no project is selected (GET param "prj" is missing) + * + * @global object $oHtml + * @global array $aParams + * + * @param string $sClass css class for the button; default: "btn btn-default" + * @return bool|string */ -function aPrjHome($sClass = "btn btn-default") +function aPrjHome(string $sClass = "btn btn-default"): bool|string { global $aParams, $oHtml; + if (!array_key_exists("prj", $aParams)) { return false; } @@ -96,51 +109,65 @@ function aPrjHome($sClass = "btn btn-default") require_once("./classes/project.class.php"); $oPrj = new project($aParams["prj"]); - return $oHtml->getLinkButton(array( + return $oHtml->getLinkButton([ 'href' => '/deployment/' . $aParams["prj"] . '/', 'icon' => 'project', 'class' => $sClass, 'label' => $oPrj->getLabel(), - )); + ]); } /** - * get go back link as button + * Get go back link as button + * + * @global object $oHtml + * + * @param string $sClass css class for the button; default: "btn btn-default" * @return string */ -function aGoback($sClass = "btn btn-default") +function aGoback(string $sClass = "btn btn-default"): string { global $oHtml; - return $oHtml->getLinkButton(array( + return $oHtml->getLinkButton([ 'href' => '#', 'onclick' => 'history.back();', 'title' => t("back"), 'icon' => 'back', 'class' => $sClass, 'label' => t("back") - )); + ]); } + /** - * get go top link as button + * Get go top link as button + * + * @global object $oHtml + * + * @param string $sClass css class for the button; default: "scroll-link btn btn-default" * @return string */ -function aGotop($sClass = "scroll-link btn btn-default") +function aGotop($sClass = "scroll-link btn btn-default"): string { global $oHtml; - return $oHtml->getLinkButton(array( + return $oHtml->getLinkButton([ 'href' => '#top', 'class' => $sClass, 'title' => t("gotop"), 'icon' => 'gotop', 'label' => ' ' - )); + ]); } /** - * get array top left navigation + * Get array for top left navigation. + * It is an empty array if no authenticated user was found. + * + * @global object $oHtml + * @global array $aParams + * * @return array */ -function getTopNavLeft($aEmbed = []) +function getTopNavLeft(): array { global $aParams, $oHtml; $aReturn = []; @@ -227,10 +254,14 @@ function getTopNavLeft($aEmbed = []) } /** - * get array top left navigation + * Get array for top right navigation. + * + * @global object $oHtml + * @global array $aParams + * * @return array */ -function getTopNavRight() +function getTopNavRight(): array { global $aParams, $oHtml; $aReturn = []; @@ -273,12 +304,14 @@ function getTopNavRight() } /** - * get h2 headline with action - * @global type $aParams + * Gett h2 headline with action + * + * @global array $aParams + * * @param string $sLinkClass classname for links; default: "" (adds class="btn btn-default") * @return string */ -function getBreadcrumb($sLinkClass = "") +function getBreadcrumb(string $sLinkClass = ""): string { global $aParams, $oHtml; $sReturn = ''; @@ -309,10 +342,10 @@ function getBreadcrumb($sLinkClass = "") } /** - * get version info + * Get version info by detecting a json file in approot * @return string */ -function getVersioninfo() +function getVersioninfo(): string { $sMyRev = ""; @@ -324,19 +357,22 @@ function getVersioninfo() } return ($sMyRev ? $sMyRev . ' @ ' : '') . php_uname("n"); } + /** - * translate function - * @global type $aConfig + * translate function. Get translated text by given key. + * + * @global array $aConfig * @staticvar array $aLang + * * @param string $s text * @return string */ -function t($s) +function t(string $s): string { global $aConfig; - static $aLang = array(); + static $aLang = []; - if (!is_array($aConfig) || !array_key_exists("lang", $aConfig)) { + if (!isset($aConfig["lang"])) { die("ERROR: \$aConfig[\"lang\"] does not exist.\n"); } if (!count($aLang)) { @@ -355,11 +391,13 @@ function t($s) } /** - * enter user and comment - * @global type $aParams - * @return string + * Get html code for a form to enter user and comment + * + * @global array $aParams + * + * @return string The HTML code */ -function enterDeployinfos() +function enterDeployinfos(): string { global $aParams; $sIdUser = "inputUser"; diff --git a/public_html/deployment/index.php b/public_html/deployment/index.php index 8bc8d030aefe545edf640bfd915ab675e1dd0fa4..70b8f86e851fc6fc54412b05d3eaf1fc04e719d0 100644 --- a/public_html/deployment/index.php +++ b/public_html/deployment/index.php @@ -1,4 +1,15 @@ <?php +/* ###################################################################### + + IML DEPLOYMENT + + MAIN FILE FOR WEB UI + + --------------------------------------------------------------------- + 2013-11-nn Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax + ###################################################################### */ define("APP_VERSION", '2.0'); @@ -109,7 +120,7 @@ if($oUser->getUsername()){ /* $sTopRight.='' .'<li >' - .$oHtml->getLink(array( + .$oHtml->getLink([ 'href'=>'#', // 'onclick'=>'toggleShellWindow(\''.$CI_plugins->getHtmlOutIdWrapper().'\', this);', 'onclick'=>'toggleShellWindow(\''.$CI_plugins->getHtmlOutId().'\', this);', @@ -117,7 +128,7 @@ if($oUser->getUsername()){ 'aria-expanded'=>'false', 'icon'=> (isset($aPluginConfig['icon']) ? $aPluginConfig['icon'] : ''), 'label'=>$sPlugin, - )) + ]) .'</li>' ; */ @@ -193,7 +204,7 @@ if ($oUser->hasPermission('page_'.$sAction)){ if ($oUser->getUsername()){ require_once("./classes/actionlog.class.php"); - $aFilter=array('limit'=>'0, 10'); + $aFilter=['limit'=>'0, 10']; if ($sPrj && $sPrj!="all"){ $aFilter['project']=$sPrj; } @@ -254,7 +265,7 @@ $aTopnav=getTopNavLeft(); $aReplace['{{NAVI_TOP}}']='' . $renderAdminLTE->addWrapper( 'nav', ['class'=>'main-header navbar navbar-expand navbar-white navbar-light'], - $renderAdminLTE->getTopNavigation($aTopnav,false, array_merge($aNavRight, getTopNavRight()), false) + $renderAdminLTE->getTopNavigation($aTopnav,[], array_merge($aNavRight, getTopNavRight()), []) // add 2nd navbar if needed ) ; diff --git a/public_html/deployment/index_v1.php b/public_html/deployment/index_v1.php deleted file mode 100644 index 0257f34ef7baad382af24302d67a60d7db31ec0d..0000000000000000000000000000000000000000 --- a/public_html/deployment/index_v1.php +++ /dev/null @@ -1,191 +0,0 @@ -<?php - -/* ###################################################################### - - IML DEPLOYMENT - - webgui - index file - controller like - ensure that you activated the rewrite rules - - RewriteEngine on - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_URI} !^/server-status$ - RewriteRule ^(.*)$ index.php [QSA,L] - - --------------------------------------------------------------------- - 2013-11-08 Axel <axel.hahn@iml.unibe.ch> - ###################################################################### */ - -session_start(); - -ini_set('display_errors', 1); -ini_set('display_startup_errors', 1); -error_reporting(E_ALL); - -require_once("./classes/page.class.php"); -require_once("./classes/plugins_renderer.class.php"); - -// detect first run -$bFirstRun=!file_exists("../../config/config_custom.php") || !file_exists("../../config/inc_user2roles.php"); - -require_once("../../config/inc_projects_config.php"); -require_once("./classes/logger.class.php"); -require_once("./classes/user.class.php"); -global $oCLog; -$oCLog = new logger(); -$oCLog->enableDebugByIp($aConfig['showdebug']['ip']); -require_once("./inc_functions.php"); -require_once("./classes/htmlguielements.class.php"); -$oHtml=new htmlguielements(); - -$sPrj = ""; -$sAction = "overview"; - -// ---------------------------------------------------------------------- -// check params -// ---------------------------------------------------------------------- - -if (array_key_exists("prj", $aParams)) { - $sPrj = $aParams["prj"]; -} -if (array_key_exists("action", $aParams)) { - if (file_exists(__DIR__ . '/pages/act_' . $aParams["action"] . ".php")) { - $sAction = $aParams["action"]; - } -} -$oCLog->add("parsing params " - . '<pre>GET '.print_r($_GET, true).'</pre>' - . '<pre>POST '.print_r($_POST, true).'</pre>' - . '<pre>aParams: '.print_r($aParams, true).'</pre>' - ); - -if($bFirstRun){ - $sAction='installer'; -} - -// ---------------------------------------------------------------------- -// html header -// ---------------------------------------------------------------------- - -$sHeader = "\n<!-- generated CSS for phases -->\n<style>\n"; -foreach ($aConfig["phases"] as $sPhase => $aData) { - $sHeader.=array_key_exists("bgdark", $aData["css"]) ? 'th.' . $sPhase . '{' . $aData["css"]["bgdark"] . '}' : ''; - $sHeader.=array_key_exists("bglight", $aData["css"]) ? 'td.' . $sPhase . ', div.' . $sPhase . '{' . $aData["css"]["bglight"] . '}' : ''; - $sHeader.=array_key_exists("bgbutton", $aData["css"]) ? 'a.' . $sPhase . ',a.' . $sPhase . ':hover,button.' . $sPhase . ',button.' . $sPhase . ':hover{' . $aData["css"]["bgbutton"] . '}' : ''; -} -$sHeader.="</style>\n"; - -// add shellcmd files -$sShellOuptut=''; -$sTopRight=''; - -$CI_plugins=new plugin_renderer(isset($aConfig['plugins']) ? $aConfig['plugins'] : []); -$CI_plugins->setType('shellcmd'); - -$aEnabledShellPlugins=$CI_plugins->getEnabledPlugins('shellcmd'); -$sHeader.= count($aEnabledShellPlugins) - ? '' - ."\n<!-- for shellcmd plugins -->\n" - .'<script src="/vendor/axelhahn/js/ubd.class.js"></script>'."\n" - .'<script src="/vendor/winbox/0.2.82/winbox.min.js"></script>'."\n" - .'<link rel="stylesheet" type="text/css" href="/vendor/winbox/0.2.82/winbox.min.css"/>'."\n" - ."<!-- shellcmd scripts -->\n" - : '' - ; -foreach ($aEnabledShellPlugins as $sPlugin){ - if ($CI_plugins->testPlugin($sPlugin)){ - $aPluginConfig=$CI_plugins->getPluginConfig(); - $sHeader.=$CI_plugins->getHtmlLoadScript('render.js'); - $sShellOuptut.=$CI_plugins->getHtmlOutwindow(); - $sTopRight.='' - .'<li >' - .$oHtml->getLink(array( - 'href'=>'#', - // 'onclick'=>'toggleShellWindow(\''.$CI_plugins->getHtmlOutIdWrapper().'\', this);', - 'onclick'=>'toggleShellWindow(\''.$CI_plugins->getHtmlOutId().'\', this);', - 'role'=>'button', - 'aria-expanded'=>'false', - 'icon'=> (isset($aPluginConfig['icon']) ? $aPluginConfig['icon'] : ''), - 'label'=>$sPlugin, - )) - .'</li>' - ; - } -} - -// ---------------------------------------------------------------------- -// html body -// ---------------------------------------------------------------------- - -$sTopArea=getTopArea(['right'=>$sTopRight]); -$sBanner=isset($aConfig['banner']) && $aConfig['banner'] ? '<div class="alert alert-info">'.$aConfig['banner'].'</div>' : ''; -$sTopAction=getAction(); - -// ------ action -$oUser=new user(); -if (isset($aConfig["auth"]['forceuser']) && $aConfig["auth"]['forceuser']){ - $oCLog->add("Found config -> auth -> forceuser: using fake identity [".$aConfig["auth"]['forceuser'].']', "warning"); - $oUser->setUser($aConfig["auth"]['forceuser']); -} - -if ($oUser->hasPermission('page_'.$sAction)){ - - $sActionFile = __DIR__ . '/pages/act_' . $sAction . ".php"; - - $oCLog->add("including $sActionFile"); - ob_start(); - if (!@include($sActionFile)) { - include("./pages/error_404.php"); - } - $sPhpOut = ob_get_contents(); - ob_end_clean(); - $oCLog->add("including done $sActionFile"); - - $oCLog->add("adding actionlog.class"); - - if ($oUser->getUsername()){ - require_once("./classes/actionlog.class.php"); - $aFilter=array('limit'=>'0, 10'); - if ($sPrj && $sPrj!="all"){ - $aFilter['project']=$sPrj; - } - $oLog=new Actionlog($sPrj); - $sPhpOut.='<div class="logs">' . $oLog->renderLogs($aFilter).'</div>'; - } - $oCLog->add("adding actionlog.class done"); -} else { - $sPhpOut=$oUser->showDenied(); - // return false; -} - -// ---------------------------------------------------------------------- -// render page -// ---------------------------------------------------------------------- - -$oCLog->add("Finally: rendering page ..."); - -$sPhpOut = ' - <br> - ' - . $sTopArea - . $sShellOuptut - .' - <div id="content"> - ' . $sBanner . $sTopAction . ' - ' . $sPhpOut . ' - </div> - <div id="footer"> - '.t("menu-brand").' © 2013-' . date("Y") . ' <a href="https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/" target="_blank">Institut für Medizinische Lehre; Universität Bern</a> - </div> - - ' - .$oCLog->render(); - -$oPage = new Page(); -$oPage->addResponseHeader("Pragma: no-cache"); -$oPage->setOutputtype('html'); -$oPage->setHeader($sHeader); -$oPage->addJsOnReady(''); - -$oPage->setContent($sPhpOut); -echo $oPage->render(); diff --git a/public_html/deployment/pages/act_rollback.php b/public_html/deployment/pages/__unused__act_rollback.php similarity index 96% rename from public_html/deployment/pages/act_rollback.php rename to public_html/deployment/pages/__unused__act_rollback.php index e61539d8b1c35d130470cdd5a4a1ed13d9a7f9d0..d776bca8b4e9ddd07d0bf6da8705262f7d2dbb61 100644 --- a/public_html/deployment/pages/act_rollback.php +++ b/public_html/deployment/pages/__unused__act_rollback.php @@ -116,4 +116,3 @@ if (array_key_exists("confirm", $aParams)) { // -- Ausgabe echo $sOut; -?> diff --git a/public_html/deployment/pages/act_about.php b/public_html/deployment/pages/act_about.php index 612929da07dff7675163ea4a71edb957188466a8..330c26a0e78feda1aead489143c3e922dcf0fac8 100644 --- a/public_html/deployment/pages/act_about.php +++ b/public_html/deployment/pages/act_about.php @@ -8,43 +8,46 @@ --------------------------------------------------------------------- 2023-12-19 Axel <axel.hahn@unibe.ch> + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ -$oHtml = new htmlguielements($sPrj); +$oHtml = new htmlguielements(); -$BODY= -$renderAdminLTE->addRow( - $renderAdminLTE->addCol( - $renderAdminLTE->getCard([ - 'type'=>'dark', - 'variant'=>'outline', - 'text'=>'<h3>IML CI server</h3>' - .$oHtml->getTable([ - 'body'=>[ - [t('page-about-version'), getVersioninfo()], - [t('page-about-author'), 'Institute for Medical Education * University of Bern'], - [t('page-about-license'), 'GNU GPL 3.0'], - [t('page-about-source'), '<a href="https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/">https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/</a>'], - [t('page-about-php'), phpversion()], - ] +$BODY = + $renderAdminLTE->addRow( + $renderAdminLTE->addCol( + $renderAdminLTE->getCard([ + 'type' => 'dark', + 'variant' => 'outline', + 'text' => '<h3>IML CI server</h3>' + . $oHtml->getTable([ + 'body' => [ + [t('page-about-version'), getVersioninfo()], + [t('page-about-author'), 'Institute for Medical Education * University of Bern'], + [t('page-about-license'), 'GNU GPL 3.0'], + [t('page-about-source'), '<a href="https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/">https://git-repo.iml.unibe.ch/iml-open-source/imldeployment/</a>'], + [t('page-about-php'), phpversion()], + ] + ]), ]), - ]), 8 - ) - .$renderAdminLTE->addCol( - $renderAdminLTE->getCard([ - 'type'=>'dark', - 'variant'=>'outline', - 'text'=>'<h3>Components</h3>' - .$oHtml->getTable([ - 'body'=>[ - ['AdminLTE', '<a href="https://adminlte.io/">https://adminlte.io/</a>'], - ['jquery 3.6.1', '<a href="https://jquery.com/">https://jquery.com/</a>'], - ['font-awesome 6.4.0', '<a href="https://fontawesome.com/">https://fontawesome.com/</a>'], - ['visjs 4.21.0', '<a href="https://visjs.org">https://visjs.org</a>'], - ['winbox 0.2.82', '<a href="https://nextapps-de.github.io/winbox/">https://nextapps-de.github.io/winbox/</a>'], - ] + 8 + ) + . $renderAdminLTE->addCol( + $renderAdminLTE->getCard([ + 'type' => 'dark', + 'variant' => 'outline', + 'text' => '<h3>Components</h3>' + . $oHtml->getTable([ + 'body' => [ + ['AdminLTE', '<a href="https://adminlte.io/">https://adminlte.io/</a>'], + ['jquery 3.6.1', '<a href="https://jquery.com/">https://jquery.com/</a>'], + ['font-awesome 6.4.0', '<a href="https://fontawesome.com/">https://fontawesome.com/</a>'], + ['visjs 4.21.0', '<a href="https://visjs.org">https://visjs.org</a>'], + ['winbox 0.2.82', '<a href="https://nextapps-de.github.io/winbox/">https://nextapps-de.github.io/winbox/</a>'], + ] + ]), ]), - ]), 4 - ) -); + 4 + ) + ); diff --git a/public_html/deployment/pages/act_accept.php b/public_html/deployment/pages/act_accept.php index cd9c666204cb8b7b3eee7be71ca4a64b68158042..1a0175e39853736c946cbc28648a3246007ea430 100644 --- a/public_html/deployment/pages/act_accept.php +++ b/public_html/deployment/pages/act_accept.php @@ -8,6 +8,8 @@ --------------------------------------------------------------------- 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ require_once("./classes/project_gui.class.php"); @@ -21,13 +23,13 @@ if (array_key_exists("par3", $aParams)) { $sOut = ''; if (array_key_exists("confirm", $aParams)) { - $sOut.=$oPrj->accept($sPhase); + $sOut .= $oPrj->accept($sPhase); } else { if (!$sPhase) { - $sOut.=$oHtml->getBox("error", t("error-no-phase")); + $sOut .= $oHtml->getBox("error", t("error-no-phase")); } else { if (!$oPrj->canAcceptPhase($sPhase)) { - $sOut.= sprintf(t("page-accept-error-cannot-accept-phase"), $sPhase); + $sOut .= sprintf(t("page-accept-error-cannot-accept-phase"), $sPhase); } else { $aPhaseData = $oPrj->getPhaseInfos($sPhase); $aConfigPrj = $oPrj->getConfig(); @@ -37,16 +39,16 @@ if (array_key_exists("confirm", $aParams)) { $aPhaseData2 = $oPrj->getPhaseInfos($sNext); if ( - array_key_exists("revision", $aPhaseData2["onhold"]) && $aPhaseData2["onhold"]["revision"] == $aPhaseData["deployed"]["revision"] + array_key_exists("revision", $aPhaseData2["onhold"]) && $aPhaseData2["onhold"]["revision"] == $aPhaseData["deployed"]["revision"] ) { - $sOut.=$oHtml->getBox("warning", sprintf(t("page-accept-warning-version-exists-in-next-queue"), $sNext, $sPhase)); + $sOut .= $oHtml->getBox("warning", sprintf(t("page-accept-warning-version-exists-in-next-queue"), $sNext, $sPhase)); } if ( - array_key_exists("revision", $aPhaseData2["ready2install"]) && $aPhaseData2["ready2install"]["revision"] == $aPhaseData["deployed"]["revision"] + array_key_exists("revision", $aPhaseData2["ready2install"]) && $aPhaseData2["ready2install"]["revision"] == $aPhaseData["deployed"]["revision"] ) { - $sOut.=$oHtml->getBox("warning", sprintf(t("page-accept-warning-version-exists-in-next-repo"), $sNext, $sPhase)); + $sOut .= $oHtml->getBox("warning", sprintf(t("page-accept-warning-version-exists-in-next-repo"), $sNext, $sPhase)); } - $sOut.=' + $sOut .= ' <table> <thead> <tr> @@ -79,35 +81,36 @@ if (array_key_exists("confirm", $aParams)) { '; // Eingabe Kommentare zum Deployment - $sOut.=' + $sOut .= ' <hr> <p> ' . t("url") . ': <a href="' . $sUrl . '">' . $sUrl . '</a><br> ' . sprintf(t("page-accept-info"), $sPhase, $sPhase, $sNext, $sNext) . ' </p>'; - $sOut.=' + $sOut .= ' <form action="?" method="post" enctype="multipart/form-data"> <input type="hidden" name="confirm" value="1"> <fieldset> - '.aGoback() - // .$oPrj->renderLink('accept', $sPhase) - .'<button type="submit" class="btn btn-large '.$sNext.'" title="'.sprintf(t("accept-hint"), $sPhase, $sNext).'"' - .'>' . $oHtml->getIcon('accept') . sprintf(t("accept"), $sPhase, $sNext) . '</button>' - .' + ' . aGoback() + // .$oPrj->renderLink('accept', $sPhase) + . '<button type="submit" class="btn btn-large ' . $sNext . '" title="' . sprintf(t("accept-hint"), $sPhase, $sNext) . '"' + . '>' . $oHtml->getIcon('accept') . sprintf(t("accept"), $sPhase, $sNext) . '</button>' + . ' </fieldset> </form> '; - $BODY= - $renderAdminLTE->addRow( - $renderAdminLTE->addCol( - $renderAdminLTE->getCard([ - 'type'=>'success', - 'variant'=>'outline', - 'text'=>$sOut, - ]), 12 - ) - ); - + $BODY = + $renderAdminLTE->addRow( + $renderAdminLTE->addCol( + $renderAdminLTE->getCard([ + 'type' => 'success', + 'variant' => 'outline', + 'text' => $sOut, + ]), + 12 + ) + ); + } } } @@ -116,4 +119,3 @@ if (array_key_exists("confirm", $aParams)) { // -- Ausgabe echo $sOut; -?> diff --git a/public_html/deployment/pages/act_build.php b/public_html/deployment/pages/act_build.php index 6d03699eda0e3cd0ae971fb54efb620091412ca6..00c9cc94972df2e820f9f4d19ef6f95fd8ee5ede 100644 --- a/public_html/deployment/pages/act_build.php +++ b/public_html/deployment/pages/act_build.php @@ -27,7 +27,7 @@ if (array_key_exists("branchname", $aParams)) { $sBranchname = $aParams["branchname"]; $oPrj->setBranchname($aParams["branchname"]); } else { - $sBranchname = $oPrj->getBranchname(true); + $sBranchname = $oPrj->getBranchname(); } if (!array_key_exists("confirm", $aParams)) { @@ -36,60 +36,60 @@ if (!array_key_exists("confirm", $aParams)) { // ------------------------------------------------------------ $sNext = $oPrj->getNextPhase(); $aPhaseData2 = $oPrj->getPhaseInfos($sNext); - $sBranchontarget=isset($aPhaseData2["ready2install"]["branch"]) ? $aPhaseData2["ready2install"]["branch"]: ''; + $sBranchontarget = isset($aPhaseData2["ready2install"]["branch"]) ? $aPhaseData2["ready2install"]["branch"] : ''; print_r($sBranchname); $bIgnoreCache = isset($aParams['reloadBranches']) ? $aParams['reloadBranches'] : false; // $sOut.='<p>Re-Read Branches (ignore caching) - '.($bIgnoreCache ? "JA" : "mein" ).'</p>'; - $sOut.='<p>' . sprintf(t("page-build-info"), $sNext, $sNext) . '</p>'; + $sOut .= '<p>' . sprintf(t("page-build-info"), $sNext, $sNext) . '</p>'; $sRevison = false; $aRepodata = $oPrj->getRepoRevision(true); if (isset($aRepodata["revision"])) { $sRevison = $aRepodata["revision"]; if ( - array_key_exists("revision", $aPhaseData2["onhold"]) && $aPhaseData2["onhold"]["revision"] == $sRevison + array_key_exists("revision", $aPhaseData2["onhold"]) && $aPhaseData2["onhold"]["revision"] == $sRevison ) { - $sOut.=$oHtml->getBox("warning", "In der Queue von [$sNext] ist die Version bereits $sRevison vorhanden!"); + $sOut .= $oHtml->getBox("warning", sprintf(t("page-build-warning-version-exists"), $sNext, $sRevison)); } if ( - array_key_exists("revision", $aPhaseData2["ready2install"]) && $aPhaseData2["ready2install"]["revision"] == $sRevison + array_key_exists("revision", $aPhaseData2["ready2install"]) && $aPhaseData2["ready2install"]["revision"] == $sRevison ) { - $sOut.=$oHtml->getBox("warning", "Im Repo von [$sNext] ist die Version $sRevison bereits vorhanden!"); + $sOut .= $oHtml->getBox("warning", sprintf(t("page-build-warning-version-is-installed"), $sNext, $sRevison)); } } - $sOut.=' + $sOut .= ' <table> <thead> <tr> - <th class="versioncontrol" colspan="3">' . $oHtml->getIcon('repository').t("versioncontrol") . '<br></th> + <th class="versioncontrol" colspan="3">' . $oHtml->getIcon('repository') . t("versioncontrol") . '<br></th> <th> </th> - <th class="' . $sNext . '" colspan="2">' . $oHtml->getIcon('phase').$sNext . '</th> + <th class="' . $sNext . '" colspan="2">' . $oHtml->getIcon('phase') . $sNext . '</th> </tr> </thead> <tbody> <tr> <td class=""> ' . $oPrj->renderSelectRemoteBranches(false, $bIgnoreCache) . ' - '.( - $sBranchontarget && $sBranchontarget != $sBranchname - ? ' + ' . ( + $sBranchontarget && $sBranchontarget != $sBranchname + ? ' <form action="?" method="post" enctype="multipart/form-data"> <input type="hidden" name="reloadBranches" value="1"> <input type="hidden" name="branchname" value="' . $sBranchontarget . '"> - '.sprintf(t('page-build-branch-on-target'), $sBranchontarget).'<br> + ' . sprintf(t('page-build-branch-on-target'), $sBranchontarget) . '<br> <button type="submit" class="btn btn-default">' . sprintf(t('page-build-switch-to-target'), $sBranchontarget) . '</button> </form> <br><hr> ' - : '' - ).' + : '' + ) . ' <form action="?" method="post" enctype="multipart/form-data"> <input type="hidden" name="reloadBranches" value="1"> <input type="hidden" name="branchname" value="' . $sBranchname . '"> <fieldset> - <button type="submit" class="btn btn-default">' . $oHtml->getIcon('refresh').t("page-build-reload-branches") . '</button> + <button type="submit" class="btn btn-default">' . $oHtml->getIcon('refresh') . t("page-build-reload-branches") . '</button> </fieldset> </form> @@ -117,7 +117,7 @@ if (!array_key_exists("confirm", $aParams)) { // Eingabe Kommentare zum Deployment if ($sRevison) { - $sOut.=' + $sOut .= ' <form action="?" method="post" enctype="multipart/form-data"> <input type="hidden" name="confirm" value="1"> <input type="hidden" name="branchname" value="' . $sBranchname . '"> @@ -125,20 +125,21 @@ if (!array_key_exists("confirm", $aParams)) { <hr> --> <fieldset> - '.aGoback().' - <button type="submit" class="btn btn-large '.$sNext.'" >' . $oHtml->getIcon('build').sprintf(t("page-build-buttonlabel"), $sNext) . '</button> + ' . aGoback() . ' + <button type="submit" class="btn btn-large ' . $sNext . '" >' . $oHtml->getIcon('build') . sprintf(t("page-build-buttonlabel"), $sNext) . '</button> </fieldset> </form> '; } - $BODY= + $BODY = $renderAdminLTE->addRow( $renderAdminLTE->addCol( $renderAdminLTE->getCard([ - 'type'=>'success', - 'variant'=>'outline', - 'text'=>$sOut, - ]), 12 + 'type' => 'success', + 'variant' => 'outline', + 'text' => $sOut, + ]), + 12 ) ); @@ -186,17 +187,17 @@ if (!array_key_exists("confirm", $aParams)) { $sAjaxFile = $aParams["prj"] . "_" . $aParams["action"]; $sDivname = "outAjax"; $sUrlStartAction = "/deployment/?" - . "&prj=" . $aParams["prj"] - . "&action=" . $aParams["action"] - . "&confirm=" . $aParams["confirm"] - . "&branchname=" . $aParams["branchname"] - . "&ajax=" . $sAjaxFile - . "&run=1" + . "&prj=" . $aParams["prj"] + . "&action=" . $aParams["action"] + . "&confirm=" . $aParams["confirm"] + . "&branchname=" . $aParams["branchname"] + . "&ajax=" . $sAjaxFile + . "&run=1" ; - $sUrlFile = "/webservice/getfile.php?". "&ajax=" . $sAjaxFile; - - $sOut.= '<div id="' . $sDivname . '"></div>' - . '<script> + $sUrlFile = "/webservice/getfile.php?" . "&ajax=" . $sAjaxFile; + + $sOut .= '<div id="' . $sDivname . '"></div>' + . '<script> var iRepeat=2000; // start build process diff --git a/public_html/deployment/pages/act_checkssh.php b/public_html/deployment/pages/act_checkssh.php index 49c0aa76ff369177445135403d99738d720aa340..c7048af370e5b7d2dab11ee4a0045030957cbe4b 100644 --- a/public_html/deployment/pages/act_checkssh.php +++ b/public_html/deployment/pages/act_checkssh.php @@ -8,18 +8,17 @@ --------------------------------------------------------------------- 2018-02-01 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ -$sOK='<span class="ok">' . t('ok') . '</span>'; -$sFAILED='<span class="error">' . t('error') . '</span>'; -$aErrors=array(); +$sOK = '<span class="ok">' . t('ok') . '</span>'; +$sFAILED = '<span class="error">' . t('error') . '</span>'; +$aErrors = []; global $sOK; global $sFAILED; global $aErrors; -// test only -// $aConfig['mirrorPackages']=array('puppet' => array('target' => 'ladmin@calcium.iml.unibe.ch:/share/imldeployment'),); - // ---------------------------------------------------------------------- // functions // ---------------------------------------------------------------------- @@ -29,19 +28,21 @@ global $aErrors; * * @global string $sOK * @global string $sFAILED + * * @param string $sCmd command to execute * @return string */ -function myExec($sCmd){ +function myExec(string $sCmd): string +{ global $sOK, $sFAILED, $aErrors; - $sReturn=''; - + $sReturn = ''; + exec($sCmd . " 2>&1 && echo '$sOK' || echo '$sFAILED' ", $aOut); $sReturn .= "<pre><strong>\$ $sCmd</strong><br>" - . implode('<br>', $aOut)."<br>" - . "</pre>"; - if (strpos($sReturn, $sFAILED)){ - $aErrors[]=$sCmd; + . implode('<br>', $aOut) . "<br>" + . "</pre>"; + if (strpos($sReturn, $sFAILED)) { + $aErrors[] = $sCmd; } return $sReturn; } @@ -52,15 +53,15 @@ function myExec($sCmd){ $sOut = '<h2>SSH</h2>'; -if(isset($aConfig['mirrorPackages']) && count($aConfig['mirrorPackages'])){ +if (isset($aConfig['mirrorPackages']) && count($aConfig['mirrorPackages'])) { // test puppet $sOut = '<h3>Puppet</h3>' - . '<ul>'; - foreach ($aConfig['mirrorPackages'] as $sServer=>$aSettings) { - $sCmd = 'ssh ' . preg_replace('#\:\/.*#', '' , $aSettings['target']) . ' echo "hello from \`hostname -f\`"'; + . '<ul>'; + foreach ($aConfig['mirrorPackages'] as $sServer => $aSettings) { + $sCmd = 'ssh ' . preg_replace('#\:\/.*#', '', $aSettings['target']) . ' echo "hello from \`hostname -f\`"'; $sOut .= '<li><strong>' . $sServer . '</strong> ' . myExec($sCmd); } - $sOut.='</ul>'; + $sOut .= '</ul>'; } // ... then loop over all projects ... @@ -69,36 +70,36 @@ foreach ($oPrj1->getProjects() as $sPrj) { $oPrj = new project($sPrj); $aPrjConfig = $oPrj->getConfig(); $sOut .= '<h3>' . $oPrj->getLabel() . '</h3>' - // . '<pre>'.print_r($aPrjConfig, 1).'</pre>' - . '<ul>'; + // . '<pre>'.print_r($aPrjConfig, 1).'</pre>' + . '<ul>'; // test repository - $sOut .= '<li>'.t('repositoryinfos').' <strong>' . $aPrjConfig['build']['url'] . '</strong> - '; - if($oPrj->getRepoRevision()){ + $sOut .= '<li>' . t('repositoryinfos') . ' <strong>' . $aPrjConfig['build']['url'] . '</strong> - '; + if ($oPrj->getRepoRevision()) { $sOut .= $sOK; } else { $sOut .= $sFAILED; $aErrors[] = $aPrjConfig['build']['url']; } - + // loop over phases ... foreach (array_keys($oPrj->getPhases()) as $sPhase) { - $sOut .= '<li>'.t('phase').' <strong>' . $sPhase . '</strong><ul>'; + $sOut .= '<li>' . t('phase') . ' <strong>' . $sPhase . '</strong><ul>'; $sDeployhosts = array_key_exists("hosts", $aPrjConfig["phases"][$sPhase]) ? $aPrjConfig["phases"][$sPhase]["hosts"] : ""; if ($sDeployhosts) { - foreach(explode(',', $sDeployhosts) as $sSingleSshTarget){ + foreach (explode(',', $sDeployhosts) as $sSingleSshTarget) { $sOut .= '<li>' . $sSingleSshTarget . '<br>'; $sCmd = 'ssh ' . $aConfig["installPackages"]["user"] . '@' . $sSingleSshTarget . ' echo "hello from \`hostname -f\`"'; - $sOut.= myExec($sCmd); + $sOut .= myExec($sCmd); } } - $sOut.='</ul>'; + $sOut .= '</ul>'; } - $sOut.='</ul>'; + $sOut .= '</ul>'; } -if(count($aErrors)){ - echo $oHtml->getBox('error', '<ol><li>'.implode('<li>', $aErrors), '</ol>'); +if (count($aErrors)) { + echo $oHtml->getBox('error', '<ol><li>' . implode('<li>', $aErrors). '</ol>'); } echo $sOut; diff --git a/public_html/deployment/pages/act_cleanup.php b/public_html/deployment/pages/act_cleanup.php index 33d53bffbfbc0b5ee8cae6ca7c26d79dd8d9ff94..9acf3782af5cc34f15bb379ea324480c2de0e518 100644 --- a/public_html/deployment/pages/act_cleanup.php +++ b/public_html/deployment/pages/act_cleanup.php @@ -36,4 +36,3 @@ $sOut = ' // -- Ausgabe echo $sOut; -?> diff --git a/public_html/deployment/pages/act_delete.php b/public_html/deployment/pages/act_delete.php index cbd4d97ca3b922d484a1adec5851bc031745f5f2..b2d9ada36e24cd001216c4dfb33b777cba8a4dde 100644 --- a/public_html/deployment/pages/act_delete.php +++ b/public_html/deployment/pages/act_delete.php @@ -11,6 +11,8 @@ --------------------------------------------------------------------- 2014-03-24 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ require_once("./classes/project_gui.class.php"); @@ -26,80 +28,80 @@ if (!array_key_exists("confirm", $aParams)) { // ----- form to confirm deletion $sOut .= '<p>' . t('page-delete-project-introtext') . '</p>' - . $oPrj->renderVersionUsage() . '<br><br>'; + . $oPrj->renderVersionUsage() . '<br><br>'; $i = 0; - $aForms = array( - 'setup' => array( - 'meta' => array( + $aForms = [ + 'setup' => [ + 'meta' => [ 'method' => 'POST', 'action' => '?', - ), - 'validate' => array(), - 'form' => array( - 'input' . $i++ => array( + ], + 'validate' => [], + 'form' => [ + 'input' . $i++ => [ 'type' => 'hidden', 'name' => 'confirm', 'value' => '1', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'checkbox', 'name' => 'removeOptions', 'label' => t("page-delete-remove-options"), 'validate' => 'isastring', 'value' => 1, - 'options' => array( - 'bRemoveRepolinks' => array( + 'options' => [ + 'bRemoveRepolinks' => [ 'label' => t("page-delete-cb-label-remove-links-for-repo"), 'checked' => false, - ), - 'bRemoveArchive' => array( + ], + 'bRemoveArchive' => [ 'label' => t("page-delete-cb-label-remove-archive"), 'checked' => false, - ), - 'bRemoveConfig' => array( + ], + 'bRemoveConfig' => [ 'label' => t("page-delete-cb-label-remove-config"), 'checked' => true, - ), - ), - ), - 'markup' . $i++ => array( + ], + ], + ], + 'markup' . $i++ => [ 'type' => 'markup', 'value' => '<hr>', - ), - 'button' . $i++ => array( + ], + 'button' . $i++ => [ 'type' => 'submit', 'class' => 'btn-danger', 'value' => $oHtml->getIcon('delete') . t("page-delete-project-buttonlabel"), - ), - ), - ), - ); + ], + ], + ], + ]; $oForm = new formgen($aForms); $sOut .= $oForm->renderHtml("setup"); - $sHome= aPrjHome(); - + $sHome = aPrjHome(); + } else { // ----- delete the project - $sHome= aHome(); + $sHome = aHome(); - $aOptions = array(); - foreach (array("bRemoveRepolinks", "bRemoveArchive", "bRemoveConfig") as $sOption) { + $aOptions = []; + foreach (["bRemoveRepolinks", "bRemoveArchive", "bRemoveConfig"] as $sOption) { if (array_search($sOption, $aParams["removeOptions"]) !== false) { $aOptions[$sOption] = 1; } } - $sErrors=$oPrj->delete($aOptions); + $sErrors = $oPrj->delete($aOptions); if ($sErrors) { - $sOut.=$oHtml->getBox("error", t('page-delete-project-delete-failed') . $sErrors); + $sOut .= $oHtml->getBox("error", t('page-delete-project-delete-failed') . $sErrors); } else { - $sOut.=$oHtml->getBox("success", t('page-delete-project-delete-success')); + $sOut .= $oHtml->getBox("success", t('page-delete-project-delete-success')); } } // $sOut.= '<div id="navbuttom">' . $sHome . '</div>'; - // -- Ausgabe +// -- Ausgabe echo $sOut; diff --git a/public_html/deployment/pages/act_deploy.php b/public_html/deployment/pages/act_deploy.php index 6d0b5df987273d439330e6d61427509d389c2eb4..60044b4b39d21611316bc8f75b065571b7741901 100644 --- a/public_html/deployment/pages/act_deploy.php +++ b/public_html/deployment/pages/act_deploy.php @@ -8,6 +8,8 @@ --------------------------------------------------------------------- 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ require_once("./classes/project_gui.class.php"); @@ -50,39 +52,39 @@ if (array_key_exists("confirm", $aParams)) { // Eingabe Kommentare zum Deployment $i = 0; - $aForms = array( - 'deploy' => array( - 'meta' => array( + $aForms = [ + 'deploy' => [ + 'meta' => [ 'method' => 'POST', 'action' => '?', - ), - 'validate' => array(), - 'form' => array( - 'input' . $i++ => array( + ], + 'validate' => [], + 'form' => [ + 'input' . $i++ => [ 'type' => 'hidden', 'name' => 'confirm', 'value' => '1', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'checkbox', 'name' => 'aIgnore', 'label' => t("deploy-settings"), 'validate' => 'isastring', - 'options' => array( - 'bIgnoreDeploytimes' => array( + 'options' => [ + 'bIgnoreDeploytimes' => [ 'label' => t("page-deploy-info-ignore-deploytime"), 'checked' => false, - ), - ), - ), - 'button' . $i++ => array( + ], + ], + ], + 'button' . $i++ => [ 'type' => 'submit', 'class' => $sPhase, 'value' => $oHtml->getIcon('deploy').t("deploy"), - ), - ), - ), - ); + ], + ], + ], + ]; $oForm = new formgen($aForms); $sOut .= $oForm->renderHtml("deploy"); @@ -107,4 +109,3 @@ if (array_key_exists("confirm", $aParams)) { // -- Ausgabe echo $sOut; -?> diff --git a/public_html/deployment/pages/act_doc.php b/public_html/deployment/pages/act_doc.php index 806a54da58e8f17bd31e19ded9bd27429d4ff32f..79faa328613008d1f8de43ac3d96318d4c29f252 100644 --- a/public_html/deployment/pages/act_doc.php +++ b/public_html/deployment/pages/act_doc.php @@ -8,57 +8,56 @@ --------------------------------------------------------------------- 2013-11-19 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ require_once("./classes/classinfos.class.php"); -$aClasses = array( - "actionlog" => array("name" => "actionLog"), - "cache" => array("name" => "AhCache"), - "config-replacement" => array("name" => "configreplacement"), - "deploy-foreman" => array("name" => "deployForeman"), - "foremanapi" => array("name" => "ForemanApi"), - "formgen" => array("name" => "formgen"), - "htmlguielements" => array("name" => "htmlguielements"), - "ldap" => array("name" => "imlldap"), - "logger" => array("name" => "logger"), - "messenger" => array("name" => "messenger"), - "page" => array("name" => "Page"), - "project" => array("name" => "project"), - "projectlist" => array("name" => "projectlist"), - "rollout_base" => array("name" => "rollout_base"), - "user" => array("name" => "user"), - "vcs.git" => array("name" => "vcs"), - -); +$aClasses = [ + "actionlog" => ["name" => "actionLog"], + // "cache" => ["name" => "AhCache"), + "config-replacement" => ["name" => "configreplacement"], + "foremanapi" => ["name" => "ForemanApi"], + "formgen" => ["name" => "formgen"], + "htmlguielements" => ["name" => "htmlguielements"], + "ldap" => ["name" => "imlldap"], + "logger" => ["name" => "logger"], + "messenger" => ["name" => "messenger"], + "page" => ["name" => "Page"], + "project" => ["name" => "project"], + "projectlist" => ["name" => "projectlist"], + "rollout_base" => ["name" => "rollout_base"], + "user" => ["name" => "user"], + "vcs.git" => ["name" => "vcs"], + +]; $sOut = ''; -$sOut.='<a href="/deployment/all/doc/">Start</a> | '; +$sOut .= '<a href="/deployment/all/doc/">Start</a> | '; foreach ($aClasses as $sClassfile => $aInfos) { - $sOut.='<a href="/deployment/all/doc/' . $sClassfile . '/">' . $sClassfile . '</a> | '; + $sOut .= '<a href="/deployment/all/doc/' . $sClassfile . '/">' . $sClassfile . '</a> | '; } -$sOut.= '<hr>'; +$sOut .= '<hr>'; if (array_key_exists("par3", $aParams)) { $sClass = $aParams["par3"]; if (!array_key_exists($sClass, $aClasses)) { - $sOut.= $oHtml->getBox("error", sprintf(t("page-doc-error-class-not-configured"), $sClass, __FILE__)); + $sOut .= $oHtml->getBox("error", sprintf(t("page-doc-error-class-not-configured"), $sClass, __FILE__)); } else { require_once("./classes/$sClass.class.php"); $o = new classinfos($aClasses[$sClass]["name"]); - $sOut.=t("page-doc-info-" . $sClass) . $o->render(); + $sOut .= t("page-doc-info-" . $sClass) . $o->render(); } } else { - $sOut.=t("page-doc-info-select-class") . '<ul>'; + $sOut .= t("page-doc-info-select-class") . '<ul>'; foreach (array_keys($aClasses) as $sClassfile) { - $sOut.='<li>' - . '<a href="/deployment/all/doc/' . $sClassfile . '/">' . $sClassfile . '</a>' - . ' ' . t("page-doc-info-" . $sClassfile) - . '</li>'; + $sOut .= '<li>' + . '<a href="/deployment/all/doc/' . $sClassfile . '/">' . $sClassfile . '</a>' + . ' ' . t("page-doc-info-" . $sClassfile) + . '</li>'; } - $sOut.='</ul>'; + $sOut .= '</ul>'; } // -- Ausgabe echo $sOut; -?> - diff --git a/public_html/deployment/pages/act_htmltest.php b/public_html/deployment/pages/act_htmltest.php index 4669c917bd1fb1df8bcf633a891fffa25529ce0a..31e7f5833b3659257c88044e993e984874de4564 100644 --- a/public_html/deployment/pages/act_htmltest.php +++ b/public_html/deployment/pages/act_htmltest.php @@ -4,139 +4,143 @@ IML DEPLOYMENT - webgui - build a package + webgui - html tester --------------------------------------------------------------------- - 2014-11-14 Axel <axel.hahn@iml.unibe.ch> selector for branches - 2014-02-14 Axel <axel.hahn@iml.unibe.ch> build was "ajaxified" - 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ require_once("./classes/project.class.php"); /** - * helper: render html code for a table row + * helper: get html code for a table row * @global htmlguielements $oHtml * @param string $sDescr description * @param string $sCode php code * @return string */ -function addHtmltestTest($sDescr, $sCode){ - $oHtml=new htmlguielements(); - $sOut='??'; - eval("\$sOut=$sCode;"); - +function addHtmltestTest(string $sDescr, string $sCode): string +{ + $oHtml = new htmlguielements(); + $sOut = '??'; + eval ("\$sOut=$sCode;"); + return '<tr>' - . '<td>' - . $sDescr - . '</td>' - . '<td style="padding: 0 1em;"><pre>' - . htmlentities($sCode) - . '</pre></td>' - . '<td style="padding: 0 1em;">' - .$sOut - . '</td>' - . '<td style="padding: 0 1em;"><pre>' - .str_replace('>', '><br>',htmlentities($sOut)) - . '</pre></td>' - . '</tr>'; + . '<td>' + . $sDescr + . '</td>' + . '<td style="padding: 0 1em;"><pre>' + . htmlentities($sCode) + . '</pre></td>' + . '<td style="padding: 0 1em;">' + . $sOut + . '</td>' + . '<td style="padding: 0 1em;"><pre>' + . str_replace('>', '><br>', htmlentities($sOut)) + . '</pre></td>' + . '</tr>'; } // ---------------------------------------------------------------------- // MAIN // ---------------------------------------------------------------------- -$oHtml=new htmlguielements(); +$oHtml = new htmlguielements(); // generate a List of al icons -$sIconlist=''; -$sIconlist.='<strong>Buttons</strong><br>'; -foreach ($oHtml->aCfg['buttons'] as $sLabel=>$aItems){ - $sIconlist.=$oHtml->getIcon($aItems['icon']).' - '.$sLabel.' - '.$aItems['icon'].'<br>'; +$sIconlist = ''; +$sIconlist .= '<strong>Buttons</strong><br>'; + + + +foreach ($oHtml->aCfg['buttons'] as $sLabel => $aItems) { + $sIconlist .= $oHtml->getIcon((string)$aItems['icon']) . ' - ' . $sLabel . ' - ' . $aItems['icon'] . '<br>'; } -$sIconlist.='<br><strong>Icons</strong><br>'; -foreach ($oHtml->aCfg['icons'] as $sLabel=>$sIcon){ - $sIconlist.=$oHtml->getIcon($sIcon).' - '.$sLabel.': '.$sIcon.'<br>'; +$sIconlist .= '<br><strong>Icons</strong><br>'; +foreach ($oHtml->aCfg['icons'] as $sLabel => $sIcon) { + $sIconlist .= $oHtml->getIcon($sIcon) . ' - ' . $sLabel . ': ' . $sIcon . '<br>'; } -$sRows='' - . addHtmltestTest("Box zeichnen - Fehler", "\$oHtml->getBox('error', 'errormessage')") - . addHtmltestTest("Box zeichnen - Warnung", "\$oHtml->getBox('warning', 'Message')") - . addHtmltestTest("Box zeichnen - Info", "\$oHtml->getBox('info', 'Message')") - . addHtmltestTest("Box zeichnen - OK", "\$oHtml->getBox('success', 'Message')") - - . addHtmltestTest("Icon - Fontawesome", "\$oHtml->getIcon('fa-close');") - . addHtmltestTest("Icon - Glyphicon", "\$oHtml->getIcon('glyphicon-user');") - - . '<tr>' - . '<td>pre-defined icons</td>' - . '<td>-</td>' - . '<td>'.$sIconlist.'</td>' - . '<td>-</td>' - . '</tr>' - - . addHtmltestTest("Link", "\$oHtml->getLink(array( +$sRows = '' + . addHtmltestTest("Box zeichnen - Fehler", "\$oHtml->getBox('error', 'errormessage')") + . addHtmltestTest("Box zeichnen - Warnung", "\$oHtml->getBox('warning', 'Message')") + . addHtmltestTest("Box zeichnen - Info", "\$oHtml->getBox('info', 'Message')") + . addHtmltestTest("Box zeichnen - OK", "\$oHtml->getBox('success', 'Message')") + + . addHtmltestTest("Icon - Fontawesome", "\$oHtml->getIcon('fa-close');") + + . '<tr>' + . '<td>pre-defined icons</td>' + . '<td>-</td>' + . '<td>' . $sIconlist . '</td>' + . '<td>-</td>' + . '</tr>' + + . addHtmltestTest("Link", "\$oHtml->getLink([ 'href'=>'https://www.axel-hahn.de/', 'icon'=>'fa-globe', 'label'=>'Axels Webseite', -));") - . addHtmltestTest("Link als Button", "\$oHtml->getLinkButton(array( +]);") + . addHtmltestTest("Link als Button", "\$oHtml->getLinkButton([ 'href'=>'https://www.axel-hahn.de/', 'target'=>'_blank', 'icon'=>'fa-globe', 'label'=>'Axels Webseite', -));") - . addHtmltestTest("Link als Button mit Type OK", "\$oHtml->getLinkButton(array('type'=>'ok',));") - . addHtmltestTest("Link als Button mit Type close", "\$oHtml->getLinkButton(array('type'=>'close',));") - . addHtmltestTest("Link als Button mit Type error", "\$oHtml->getLinkButton(array('type'=>'error','label'=>'Fehler'));") +]);") + . addHtmltestTest("Link als Button mit Type OK", "\$oHtml->getLinkButton(['type'=>'ok',]);") + . addHtmltestTest("Link als Button mit Type close", "\$oHtml->getLinkButton(['type'=>'close',]);") + . addHtmltestTest("Link als Button mit Type error", "\$oHtml->getLinkButton(['type'=>'error','label'=>'Fehler']);") -// . addHtmltestTest("Tabs", "\$oHtml->getNav( -// array( -// 'options' => array( + // . addHtmltestTest("Tabs", "\$oHtml->getNav( +// [ +// 'options' => [ // 'type'=>'tabs', // 'justified'=>1, -// ), -// 'tabs' => array( +// ], +// 'tabs' => [ // 'tab 1'=>'Inhalt #1', // 'tab 2'=>'Inhalt #2', -// ), -// ) +// ], +// ] // );") // . addHtmltestTest("Tabs", "\$oHtml->getNav( -// array( -// 'options' => array( +// [ +// 'options' => [ // 'type'=>'pills', // 'stacked'=>1, -// ), -// 'tabs' => array( +// ], +// 'tabs' => [ // 'tab 1'=>'Inhalt #1', // 'tab 2'=>'Inhalt #2', -// ), -// ) +// ], +// ] // );") - . addHtmltestTest("Tabelle", "\$oHtml->getTable( - array( - 'header'=>array('A', 'B'), - 'body'=>array( - array('Zelle A 1', 'Zelle B 1'), - array('Zelle A 2', 'Zelle B 2'), - array('Zelle A 3', 'Zelle B 3'), - ), -));" -) + . addHtmltestTest( + "Tabelle", + "\$oHtml->getTable( + [ + 'header'=>['A', 'B'], + 'body'=>[ + ['Zelle A 1', 'Zelle B 1'], + ['Zelle A 2', 'Zelle B 2'], + ['Zelle A 3', 'Zelle B 3'], + ], + ]);" + ) ; // ---------------------------------------------------------------------- echo '<table><thead>' . '<tr>' - . '<th>Beschreibung</th>' - . '<th>PHP-Code</th>' - . '<th>Ausgabe</th>' - . '<th>Ausgabe-Code</th>' + . '<th>Beschreibung</th>' + . '<th>PHP-Code</th>' + . '<th>Ausgabe</th>' + . '<th>Ausgabe-Code</th>' . '</tr>' -. '</thead><tbody>' - .$sRows -. '</tbody></table>' + . '</thead><tbody>' + . $sRows + . '</tbody></table>' ; diff --git a/public_html/deployment/pages/act_login.php b/public_html/deployment/pages/act_login.php index 288dbe47f17e5a9269307362b07e9c96582513d1..577102c6346af7d4d777b123d964da700ebf7a5f 100644 --- a/public_html/deployment/pages/act_login.php +++ b/public_html/deployment/pages/act_login.php @@ -8,6 +8,8 @@ --------------------------------------------------------------------- 2015-04-21 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ require_once("./inc_functions.php"); @@ -31,16 +33,16 @@ if (!$oUser->getUsername() && array_key_exists('user', $aParams)) { $oUser->authenticate(); } - // if user is logged in and credentials were sent: reload to remove post vars - if ($oUser->getUsername() && array_key_exists('user', $aParams)) { - if (array_key_exists("goback", $_SESSION)){ - $sUrl=$_SESSION["goback"]; - unset($_SESSION["goback"]); - } else { - $sUrl='/deployment'; - } - header("location: $sUrl"); +// if user is logged in and credentials were sent: reload to remove post vars +if ($oUser->getUsername() && array_key_exists('user', $aParams)) { + if (array_key_exists("goback", $_SESSION)) { + $sUrl = $_SESSION["goback"]; + unset($_SESSION["goback"]); + } else { + $sUrl = '/deployment'; } + header("location: $sUrl"); +} // ---------------------------------------------------------------------- // show infos or login form @@ -52,63 +54,63 @@ if ($oUser->getUsername()) { // ------------------------------------------------------------ // show user and roles + logoff // ------------------------------------------------------------ - $sGrouplist=''; - foreach ($oUser->getUserGroups() as $sGroupname){ - $sGrouplist.=$oHtml->getIcon('user-group').$sGroupname . '<br>'; + $sGrouplist = ''; + foreach ($oUser->getUserGroups() as $sGroupname) { + $sGrouplist .= $oHtml->getIcon('user-group') . $sGroupname . '<br>'; } - $sPermlist=''; - foreach ($oUser->getUserPermission() as $sPerm){ - $sPermlist.=$oHtml->getIcon('user-permission').$sPerm . '<br>'; + $sPermlist = ''; + foreach ($oUser->getUserPermission() as $sPerm) { + $sPermlist .= $oHtml->getIcon('user-permission') . $sPerm . '<br>'; } - - $sOut.='' - . '<h2>' . t("page-login-profile") . '</h2>' - .'<table> + + $sOut .= '' + . '<h2>' . t("page-login-profile") . '</h2>' + . '<table> <tr> - <td>'.t("page-login-userloggedin") . '</td> + <td>' . t("page-login-userloggedin") . '</td> <td><strong>' . $oUser->getUsername() . '</strong></td> </tr> <tr> - <td>'.t("page-login-usergroups").'</td> - <td>'.$sGrouplist . '</td> + <td>' . t("page-login-usergroups") . '</td> + <td>' . $sGrouplist . '</td> </tr> <tr> - <td>'.t("page-login-userperms").'</td> - <td>'.$sPermlist . '</td> + <td>' . t("page-login-userperms") . '</td> + <td>' . $sPermlist . '</td> </tr> </table>' - . '<div style="clear: both; margin-bottom: 1em;"></div>' - . ' ' . aPrjHome() . ' ' - . '<a href="?logoff=1" class="btn btn-danger">' . $oHtml->getIcon('poweroff') . t('logoff') . '</a>' - - // . '<br><br>INFO: <pre style="">roles:<br>' . print_r($oUser->getUserPermission(), true) . '</pre>' - . '</p>' - ; + . '<div style="clear: both; margin-bottom: 1em;"></div>' + . ' ' . aPrjHome() . ' ' + . '<a href="?logoff=1" class="btn btn-danger">' . $oHtml->getIcon('poweroff') . t('logoff') . '</a>' + + // . '<br><br>INFO: <pre style="">roles:<br>' . print_r($oUser->getUserPermission(), true) . '</pre>' + . '</p>' + ; } else { // ------------------------------------------------------------ // login page // ------------------------------------------------------------ - if (!isset($_SESSION['goback']) || true){ - $sUrlback=$_SERVER["HTTP_REFERER"]; - if( - strpos($sUrlback, $_SERVER["SERVER_NAME"])>0 - && !strpos($sUrlback, '/all/login/') - ){ - $_SESSION["goback"]=$sUrlback; + if (!isset($_SESSION['goback']) || true) { + $sUrlback = $_SERVER["HTTP_REFERER"]; + if ( + strpos($sUrlback, $_SERVER["SERVER_NAME"]) > 0 + && !strpos($sUrlback, '/all/login/') + ) { + $_SESSION["goback"] = $sUrlback; } } $i = 0; - require_once ("./classes/formgen.class.php"); + require_once("./classes/formgen.class.php"); - $aForms = array( - 'login' => array( - 'meta' => array( + $aForms = [ + 'login' => [ + 'meta' => [ 'method' => 'POST', 'action' => '?', - ), - 'validate' => array(), - 'form' => array( - 'input' . $i++ => array( + ], + 'validate' => [], + 'form' => [ + 'input' . $i++ => [ 'type' => 'text', 'name' => 'user', 'label' => t('page-login-username'), @@ -119,8 +121,8 @@ if ($oUser->getUsername()) { 'placeholder' => t('page-login-username'), 'autofocus' => 'autofocus', // 'inline' => '1', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'password', 'name' => 'password', 'label' => t('page-login-password'), @@ -130,41 +132,41 @@ if ($oUser->getUsername()) { 'value' => $aParams['password'], 'placeholder' => t('page-login-password'), // 'inline' => '1', - ), - 'input' . $i++ => array( + ], + 'input' . $i++ => [ 'type' => 'submit', - 'label' => "", 'name' => 'btnsave', 'label' => t("login"), 'value' => $oHtml->getIcon('sign-ok') . t("login"), - ), - ), - ) - ); + ], + ], + ] + ]; $oForm = new formgen($aForms); - $sOut.='' - . '<h2>' . t("page-login-info") . '</h2>' - . '<p>' . t("page-login-info-introtext") . '</p>' - . (array_key_exists('user', $aParams) - ? '<div class="alert alert-danger" role="alert">'.t('page-login-auth-failed').'</div>' - : '' - ) - .$oForm->renderHtml("login"); + $sOut .= '' + . '<h2>' . t("page-login-info") . '</h2>' + . '<p>' . t("page-login-info-introtext") . '</p>' + . (array_key_exists('user', $aParams) + ? '<div class="alert alert-danger" role="alert">' . t('page-login-auth-failed') . '</div>' + : '' + ) + . $oForm->renderHtml("login"); } // $sOut.= '<div id="navbuttom">' . aPrjHome() . '</div>'; // -- Ausgabe // echo $sOut; -$BODY= -$renderAdminLTE->addRow( - $renderAdminLTE->addCol('',3) - .$renderAdminLTE->addCol( - $renderAdminLTE->getCard([ - 'type'=>'', - 'variant'=>'outline', - 'text'=>$sOut, - ]),5 - ) - .$renderAdminLTE->addCol('',4) -); +$BODY = + $renderAdminLTE->addRow( + $renderAdminLTE->addCol('', 3) + . $renderAdminLTE->addCol( + $renderAdminLTE->getCard([ + 'type' => '', + 'variant' => 'outline', + 'text' => $sOut, + ]), + 5 + ) + . $renderAdminLTE->addCol('', 4) + ); diff --git a/public_html/deployment/pages/act_overview.php b/public_html/deployment/pages/act_overview.php index d96cbb833048d1e278210baa793dacd7b1f1fe0b..b6d0f8604dce0045dd6a20a523752e62f1f59a62 100644 --- a/public_html/deployment/pages/act_overview.php +++ b/public_html/deployment/pages/act_overview.php @@ -9,10 +9,12 @@ * for a single project --------------------------------------------------------------------- - 2014-11-17 Axel <axel.hahn@iml.unibe.ch> added tags and branches + 2013-11-08 Axel <axel.hahn@iml.unibe.ch> 2014-04-24 Axel <axel.hahn@iml.unibe.ch> new visual; sortorder in project overview - 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + 2014-11-17 Axel <axel.hahn@iml.unibe.ch> added tags and branches + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ @@ -24,79 +26,80 @@ if (!array_key_exists("prj", $aParams)) { require_once("./classes/projectlist.class.php"); $oPrjList = new projectlist(); - $BODY_END='<script type="text/javascript" src="/deployment/js/functions_overview.js"></script>'; + $BODY_END = '<script type="text/javascript" src="/deployment/js/functions_overview.js"></script>'; $BODY = $oPrjList->renderOverview(); } else { - + // ---------------------------------------------------------------------- // overview of a single project // ---------------------------------------------------------------------- require_once("./classes/project_gui.class.php"); $oPrj = new projectgui($aParams["prj"]); - - $iCountOfBranches=0; - $iCountOfBuildErrors=count($oPrj->getBuildErrors()); - $iCountOfpackages=count($oPrj->getVersions()); - $iCountOfPhases=count($oPrj->getActivePhases()); - - $sBuildErrorContent='<p>'.t('build-failes-hint').'</p>'; - if($iCountOfBuildErrors){ - $aTabdata=array(); - $aErrorfiles=$oPrj->getBuildErrors(); + + $iCountOfBranches = 0; + $iCountOfBuildErrors = count($oPrj->getBuildErrors()); + $iCountOfpackages = count($oPrj->getVersions()); + $iCountOfPhases = count($oPrj->getActivePhases()); + + $sBuildErrorContent = '<p>' . t('build-failes-hint') . '</p>'; + if ($iCountOfBuildErrors) { + $aTabdata = []; + $aErrorfiles = $oPrj->getBuildErrors(); rsort($aErrorfiles); - foreach ($aErrorfiles as $sNumber=>$sErrorfile){ - $aTabdata[$oHtml->getIcon('file-any').' '.$sErrorfile]='' - // . 'file: ' . $oHtml->getIcon('file-target'). ''.$sErrorfile.'<br>' - // . 'date: ' . .'<br>' - // . 'age: ' . .'<br>' - . $oPrj->getBuildErrorContent($sErrorfile); + foreach ($aErrorfiles as $sNumber => $sErrorfile) { + $aTabdata[$oHtml->getIcon('file-any') . ' ' . $sErrorfile] = '' + // . 'file: ' . $oHtml->getIcon('file-target'). ''.$sErrorfile.'<br>' + // . 'date: ' . .'<br>' + // . 'age: ' . .'<br>' + . $oPrj->getBuildErrorContent($sErrorfile); } /* - $sBuildErrorContent.=$oHtml->getNav(array( - 'options' => array( + $sBuildErrorContent.=$oHtml->getNav([ + 'options' => [ 'type'=>'pills', 'justified'=>0, - ), - 'tabs'=>$aTabdata) + ], + 'tabs'=>$aTabdata + ] ); */ - $sBuildErrorContent.=$renderAdminLTE->getTabbedContent(['tabs'=>$aTabdata]); + $sBuildErrorContent .= $renderAdminLTE->getTabbedContent(['tabs' => $aTabdata]); } else { - $sBuildErrorContent=$oHtml->getBox('info', t('build-failes-none')); + $sBuildErrorContent = $oHtml->getBox('info', t('build-failes-none')); } - $sListOfBranches=''; - foreach($oPrj->getRemoteBranches() as $aBranch){ - $sListOfBranches.='<li title="'.$aBranch['revision'].'">'.$aBranch['label'] . '</li>'; + $sListOfBranches = ''; + foreach ($oPrj->getRemoteBranches() as $aBranch) { + $sListOfBranches .= '<li title="' . $aBranch['revision'] . '">' . $aBranch['label'] . '</li>'; $iCountOfBranches++; } - if($sListOfBranches){ - $sListOfBranches='<h4>'.t('branch-select').'</h4><ol>'.$sListOfBranches.'</ol>'; + if ($sListOfBranches) { + $sListOfBranches = '<h4>' . t('branch-select') . '</h4><ol>' . $sListOfBranches . '</ol>'; } // --- generate tabs: - $aTabdata=[]; + $aTabdata = []; $aTabdata[$oHtml->getIcon('workflow') . t("way-of-packages")] = $oPrj->renderVisual(); - if($iCountOfBuildErrors){ - $aTabdata[$oHtml->getIcon('sign-error') . t("build-failes") . ($iCountOfBuildErrors ? ' <span class="badge badge-danger">'.$iCountOfBuildErrors.'</span>' : '' )]='<br>'.$sBuildErrorContent; + if ($iCountOfBuildErrors) { + $aTabdata[$oHtml->getIcon('sign-error') . t("build-failes") . ($iCountOfBuildErrors ? ' <span class="badge badge-danger">' . $iCountOfBuildErrors . '</span>' : '')] = '<br>' . $sBuildErrorContent; } - if($iCountOfpackages){ - $aTabdata[$oHtml->getIcon('package') . t("archive") . ($iCountOfpackages ? ' <span class="badge badge-secondary">'.$iCountOfpackages.'</span>' : '' )] = '<br>'.$oPrj->renderVersionUsage(); + if ($iCountOfpackages) { + $aTabdata[$oHtml->getIcon('package') . t("archive") . ($iCountOfpackages ? ' <span class="badge badge-secondary">' . $iCountOfpackages . '</span>' : '')] = '<br>' . $oPrj->renderVersionUsage(); } - if($iCountOfPhases){ - $aTabdata[$oHtml->getIcon('phase') . t('phases') .' '. ($iCountOfPhases ? '<span class="badge badge-secondary">'.$iCountOfPhases.'</span>' : '<span class="badge badge-danger">0</span>' )]='<br>' - .'<p>' - . t("page-overview-phase-infos") - . '</p>' + if ($iCountOfPhases) { + $aTabdata[$oHtml->getIcon('phase') . t('phases') . ' ' . ($iCountOfPhases ? '<span class="badge badge-secondary">' . $iCountOfPhases . '</span>' : '<span class="badge badge-danger">0</span>')] = '<br>' + . '<p>' + . t("page-overview-phase-infos") + . '</p>' . $oPrj->renderPhaseInfo() ; } $sOut = '' - .'<h3>'.t('overview-label').' '.$oPrj->renderLink("setup").'</h3><br>' - .$renderAdminLTE->getTabbedContent(['tabs'=>$aTabdata]) - ; + . '<h3>' . t('overview-label') . ' ' . $oPrj->renderLink("setup") . '</h3><br>' + . $renderAdminLTE->getTabbedContent(['tabs' => $aTabdata]) + ; /* $sPhaselinks=''; @@ -110,15 +113,16 @@ if (!array_key_exists("prj", $aParams)) { } */ - - $BODY= + + $BODY = $renderAdminLTE->addRow( $renderAdminLTE->addCol( $renderAdminLTE->getCard([ - 'type'=>'gray', - 'variant'=>'outline', - 'text'=>$sOut, - ]), 12 + 'type' => 'gray', + 'variant' => 'outline', + 'text' => $sOut, + ]), + 12 ) /* $renderAdminLTE->addCol( @@ -146,7 +150,7 @@ if (!array_key_exists("prj", $aParams)) { ) */ ); - // $sOut.='<div id="navbuttom">' . aGotop() . aHome() . '</div>'; + // $sOut.='<div id="navbuttom">' . aGotop() . aHome() . '</div>'; } // echo $sOut; diff --git a/public_html/deployment/pages/act_phase.php b/public_html/deployment/pages/act_phase.php index bbebeddb0d287258150b81657915196b299a3fe0..947583b56b887e3654cd38ae5291340f3eef0356 100644 --- a/public_html/deployment/pages/act_phase.php +++ b/public_html/deployment/pages/act_phase.php @@ -10,6 +10,7 @@ --------------------------------------------------------------------- 2013-11-08 Axel <axel.hahn@iml.unibe.ch> 2024-02-28 Axel <axel.hahn@unibe.ch> remove foreman; update replacements + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ require_once("./classes/project_gui.class.php"); @@ -25,7 +26,7 @@ if (isset($aParams["par3"])) { } if ($sPhase) { - $aWarnings=array(); + $aWarnings=[]; $sOutReplace=''; // ---------------------------------------------------------------------- @@ -65,14 +66,14 @@ if ($sPhase) { // open all/ close all if(count($aReplacements)>1){ - $sOutReplace.=$oHtml->getLinkButton(array( + $sOutReplace.=$oHtml->getLinkButton([ 'onclick'=>'$(\'.divfileinfos\').slideDown(); $(\'.expandable\').removeClass(\'closed\'); this.blur(); return false;', 'icon'=>$oHtml->getIconClass('box-down'), - )) - .$oHtml->getLinkButton(array( + ]) + .$oHtml->getLinkButton([ 'onclick'=>'$(\'.divfileinfos\').slideUp(); $(\'.expandable\').addClass(\'closed\'); this.blur(); return false;', 'icon'=>$oHtml->getIconClass('box-up'), - )) + ]) ; } @@ -86,12 +87,12 @@ if ($sPhase) { $sOutReplace.='<h4>' . // --- link filename of template to toggle details. - $oHtml->getLink(array( + $oHtml->getLink([ 'onclick'=>'$(\'#'.$sDivIdFile.'\').slideToggle(); $(this).toggleClass(\'closed\'); return false;', 'class'=>'expandable closed', 'icon'=>'templatefile', 'label'=>$tTplFile, - )) . '</h4>' + ]) . '</h4>' . '<div id="'.$sDivIdFile.'" class="divfileinfos" style="display: none;">' ; diff --git a/public_html/deployment/pages/act_setup.php b/public_html/deployment/pages/act_setup.php index e3c8df850e66fa64b8d44bbbe47e96d8d5cf2f32..602bad1c3fde6c49091669a1a742a5c5fb916ac1 100644 --- a/public_html/deployment/pages/act_setup.php +++ b/public_html/deployment/pages/act_setup.php @@ -10,62 +10,67 @@ --------------------------------------------------------------------- 2013-11-08 Axel <axel.hahn@iml.unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ require_once("./classes/project_gui.class.php"); require_once("./inc_functions.php"); $sOut = ''; -$sFakePassword='********************'; +$sFakePassword = '********************'; // items to mask -$aMask=array( - 'auth'=>array( - 'ldap'=>array( - 'PwLdapUser'=>$sFakePassword - ) - ), - 'foreman'=>array( - 'password'=>$sFakePassword - ), - 'projects'=>array( - 'ldap'=>array( - 'PwLdapUser'=>$sFakePassword - ) - ), -); +$aMask = [ + 'auth' => [ + 'ldap' => [ + 'PwLdapUser' => $sFakePassword + ] + ], + 'foreman' => [ + 'password' => $sFakePassword + ], + 'projects' => [ + 'ldap' => [ + 'PwLdapUser' => $sFakePassword + ] + ], +]; /** * hide entries in config array * @param array $aMask * @param array $aConfig - * @return type + * @return array */ -function maskEntries($aMask, $aConfig){ - foreach ($aMask as $sKey=>$aValue){ - - if (array_key_exists($sKey, $aConfig)){ - $aConfig[$sKey]=(is_array($aValue) - ? maskEntries($aMask[$sKey], $aConfig[$sKey]) - : $aMask[$sKey] +function maskEntries(array $aMask, array $aConfig): array +{ + $aReturn = $aConfig; + foreach ($aMask as $sKey => $aValue) { + + if (array_key_exists($sKey, $aReturn)) { + $aReturn[$sKey] = (is_array($aValue) + ? maskEntries($aMask[$sKey], $aReturn[$sKey]) + : $aMask[$sKey] ) ; } } - return $aConfig; + return $aReturn; } /** - * recursive replace of values in a hash + * Recursive replace of values in a hash * source: https://www.w3schools.in/php-script/recursive-array-replace-by-Key-or-Value/ * FIX: 3x "=" in if($Key === $Find) * * @param array $Array Array * @param string $Find key to scan for - * @param strin $Replace new value + * @param string $Replace new value * @return array */ -function ArrayReplace($Array, $Find, $Replace) { +function ArrayReplace(array $Array, string $Find, string $Replace): array +{ if (is_array($Array)) { foreach ($Array as $Key => $Val) { if (is_array($Array[$Key])) { @@ -85,119 +90,114 @@ function ArrayReplace($Array, $Find, $Replace) { // --------------------------------------------------------------------- if ($aParams["prj"] == "all") { - + // ------------------------------------------------------------ // general setup - overview // ------------------------------------------------------------ if (!array_key_exists("par3", $aParams)) { $oPrj = new project(); // $aTmp=maskEntries($aMask, $aConfig); - $aTmp=$aConfig; - $aTmp=ArrayReplace($aTmp, "password", $sFakePassword); - $aTmp=ArrayReplace($aTmp, "PwLdapUser", $sFakePassword); - $TITLE=t('menu-settings'); + $aTmp = $aConfig; + $aTmp = ArrayReplace($aTmp, "password", $sFakePassword); + $aTmp = ArrayReplace($aTmp, "PwLdapUser", $sFakePassword); + $TITLE = t('menu-settings'); + + $sOut .= '<pre>' . print_r($aTmp, 1) . '</pre>'; - $sOut.= '<pre>'.print_r($aTmp, 1).'</pre>'; - // print_r($aConfig); - + // ----- from HERE: generate form (experimental) $i = 0; - require_once ("./classes/formgen.class.php"); - + require_once("./classes/formgen.class.php"); + // define editable options - $aMapping=array( - 'general'=>array( - '["workDir"]'=>array('type'=>'text'), - '["versionsToKeep"]'=>array('type'=>'text', 'validate'=>'isinteger'), - '["builtsToKeep"]'=>array('type'=>'text', 'validate'=>'isinteger'), - '["lang"]'=>array('type'=>'text'), - ), - ); - foreach ($aConfig['phases'] as $sPhase => $aPhaseData){ - $aMapping['phase-'.$sPhase]=array( - '["phases"]["'.$sPhase.'"]["css"]["bgdark"]'=>array('type'=>'text'), - '["phases"]["'.$sPhase.'"]["css"]["bglight"]'=>array('type'=>'text'), - '["phases"]["'.$sPhase.'"]["css"]["bgbutton"]'=>array('type'=>'text'), - ); - /* - if (array_key_exists("deploytimes", $aConfig["phases"][$sPhase])){ - $aMapping['phase-'.$sPhase]['["phases"]["'.$sPhase.'"]["deploytimes"]']=array('type'=>'text'); - } - * - */ + $aMapping = [ + 'general' => [ + '["workDir"]' => ['type' => 'text'], + '["versionsToKeep"]' => ['type' => 'text', 'validate' => 'isinteger'], + '["builtsToKeep"]' => ['type' => 'text', 'validate' => 'isinteger'], + '["lang"]' => ['type' => 'text'], + ], + ]; + foreach ($aConfig['phases'] as $sPhase => $aPhaseData) { + $aMapping['phase-' . $sPhase] = [ + '["phases"]["' . $sPhase . '"]["css"]["bgdark"]' => ['type' => 'text'], + '["phases"]["' . $sPhase . '"]["css"]["bglight"]' => ['type' => 'text'], + '["phases"]["' . $sPhase . '"]["css"]["bgbutton"]' => ['type' => 'text'], + ]; + } - - $aForms = array( - 'setup' => array( - 'meta' => array( + + $aForms = [ + 'setup' => [ + 'meta' => [ 'method' => 'POST', 'action' => '?', - ), - 'validate' => array(), - 'form' => array( - 'input' . $i++ => array( + ], + 'validate' => [], + 'form' => [ + 'input' . $i++ => [ 'type' => 'hidden', 'name' => 'setupaction', 'value' => 'save', - ), - ), - ) - ); - foreach ($aMapping as $sPartname=>$aPartData){ - + ], + ], + ] + ]; + foreach ($aMapping as $sPartname => $aPartData) { + // add a headline - $aForms['setup']['form']['input' . $i++] = array( + $aForms['setup']['form']['input' . $i++] = [ 'type' => 'markup', - 'value' => '<h3>'.t('setup-deployment-'.$sPartname).'</h3>', - ); - + 'value' => '<h3>' . t('setup-deployment-' . $sPartname) . '</h3>', + ]; + // add input items - foreach ($aPartData as $sName=>$aFormOptions){ - - $sEval='$sCfgVal=$aConfig'.$sName.';'; - eval($sEval); - + foreach ($aPartData as $sName => $aFormOptions) { + + $sEval = '$sCfgVal=$aConfig' . $sName . ';'; + eval ($sEval); + // checks - if (!$sCfgVal){ - $sError.='<li>configration variable $sConfig'.$sName.' does not exist.</li>'; + if (!$sCfgVal) { + $sError .= '<li>configration variable $sConfig' . $sName . ' does not exist.</li>'; } // echo $sEval . ' .. ' . $sName . " :: " . $sCfgVar."<br>"; - $sFormname=str_replace('"', '' , 'aConfig'.$sName); + $sFormname = str_replace('"', '', 'aConfig' . $sName); + - - $aForms['setup']['form']['input' . $i++] = array( - 'value' => '<h3>'.t('setup-deployment-'.$sPartname).'</h3>', + $aForms['setup']['form']['input' . $i++] = [ + // 'value' => '<h3>' . t('setup-deployment-' . $sPartname) . '</h3>', 'type' => $aFormOptions['type'], 'name' => $sFormname, - 'label' => 'aConfig'.$sName, + 'label' => 'aConfig' . $sName, 'value' => $sCfgVal, // 'required' => 'required', 'validate' => 'isastring', 'title' => htmlentities($sCfgVal), 'size' => 100, 'placeholder' => htmlentities($sCfgVal), - ); + ]; } } - if ($sError){ - $sOut.=$oHtml->getBox("error", '<ul>'.$sError.'</ul>'); + if ($sError) { + $sOut .= $oHtml->getBox("error", '<ul>' . $sError . '</ul>'); } else { $oForm = new formgen($aForms); // TODO to enable form uncomment the next line // $sOut.=$oForm->renderHtml("setup"); } - + } - + if (array_key_exists("par3", $aParams)) { - + // ------------------------------------------------------------ // setup a new project // ------------------------------------------------------------ - if ($aParams["par3"]=="new") { - $TITLE=t("menu-new-project"); - $sOut.= '<p>'.t("page-setup-info-new-project-introtext").'</p><hr>'; + if ($aParams["par3"] == "new") { + $TITLE = t("menu-new-project"); + $sOut .= '<p>' . t("page-setup-info-new-project-introtext") . '</p><hr>'; $oPrj = new projectgui(); if (array_key_exists("setupaction", $aParams) && $aParams["setupaction"] == "create") { @@ -205,20 +205,21 @@ if ($aParams["prj"] == "all") { if (!$sError) { header("location: /deployment/" . $aParams["id"] . "/setup/"); } - $sOut.=$oHtml->getBox("error", $sError); + $sOut .= $oHtml->getBox("error", $sError); } - $sOut.=$oPrj->renderNewProject(); - $BODY= - $renderAdminLTE->addRow( - $renderAdminLTE->addCol( - $renderAdminLTE->getCard([ - 'type'=>'primary', - 'variant'=>'outline', - 'text'=>$sOut, - ]), 12 - ) - ); - + $sOut .= $oPrj->renderNewProject(); + $BODY = + $renderAdminLTE->addRow( + $renderAdminLTE->addCol( + $renderAdminLTE->getCard([ + 'type' => 'primary', + 'variant' => 'outline', + 'text' => $sOut, + ]), + 12 + ) + ); + } // ------------------------------------------------------------ // users and roles @@ -232,7 +233,7 @@ if ($aParams["prj"] == "all") { $aUser2Roles=$oUserCfg->getUser2Roles(); // $aUser2Projects=$oUserCfg->getUser2Projects(); - $aUsers=array(); + $aUsers=[]; $sOut.=print_r($aUser2Roles,1).'<br>'; $sRoles=''; foreach ($aUser2Roles as $sRole=>$aUserlist){ @@ -260,84 +261,85 @@ if ($aParams["prj"] == "all") { // ------------------------------------------------------------ // check lang-texts // ------------------------------------------------------------ - if ($aParams["par3"]=="checklang") { - $TITLE=t("menu-checklang"); - $sOut.='<p>'.t("page-setup-info-check-lang-intro").'</p>'; + if ($aParams["par3"] == "checklang") { + $TITLE = t("menu-checklang"); + $sOut .= '<p>' . t("page-setup-info-check-lang-intro") . '</p>'; // $sOut.=print_r($aConfig, true); - $aTmp=array(); - $aLangs=array(); - + $aTmp = []; + $aLangs = []; // --- fetch data foreach (glob($aConfig["configDir"] . '/lang/*.json') as $filename) { - $sLang=basename($filename); - $aLangs[]=$sLang; - foreach (json_decode(file_get_contents($filename), true) as $skey => $sText){ - $aTmp[$skey][$sLang]=$sText; + $sLang = basename($filename); + $aLangs[] = $sLang; + foreach (json_decode(file_get_contents($filename), true) as $skey => $sText) { + $aTmp[$skey][$sLang] = $sText; } } - + // --- generate output - $sWarnings=''; - $sErrors=''; - $sTable.='<table class="table"><thead><tr>' - . '<th>key</th>'; - foreach ($aLangs as $sLang) { - $sTable.='<th>'.$sLang.'</th>'; - } - $sTable.='</tr></thead><tbody>'; - - foreach ($aTmp as $sKey => $aTexts){ - $trid='tr'.md5($sKey); - $sTable.='<tr id="'.$trid.'"><td>'.$sKey.'</td>'; + $sWarnings = ''; + $sErrors = ''; + $sTable .= '<table class="table"><thead><tr>' + . '<th>key</th>'; + foreach ($aLangs as $sLang) { + $sTable .= '<th>' . $sLang . '</th>'; + } + $sTable .= '</tr></thead><tbody>'; + + foreach ($aTmp as $sKey => $aTexts) { + $trid = 'tr' . md5($sKey); + $sTable .= '<tr id="' . $trid . '"><td>' . $sKey . '</td>'; foreach ($aLangs as $sLang) { - if (array_key_exists($sLang, $aTmp[$sKey])){ - if (!$aTmp[$sKey][$sLang]){ - $sWarnings.='<li><a href="#'.$trid.'">'.$sLang.': key <em>'.$sKey.'</em> is empty.</a></li>'; - $sTable.='<td>'.t("empty").'</td>'; + if (array_key_exists($sLang, $aTmp[$sKey])) { + if (!$aTmp[$sKey][$sLang]) { + $sWarnings .= '<li><a href="#' . $trid . '">' . $sLang . ': key <em>' . $sKey . '</em> is empty.</a></li>'; + $sTable .= '<td>' . t("empty") . '</td>'; } else { - $sTable.='<td>'.htmlentities($aTmp[$sKey][$sLang]).'</td>'; + $sTable .= '<td>' . htmlentities($aTmp[$sKey][$sLang]) . '</td>'; } } else { - $sErrors.='<li><a href="#'.$trid.'">'.$sLang.': key <em>'.$sKey.'</em> does not exist.</a></li>'; - $sTable.='<td class="error">!!! MISS !!!</td>'; + $sErrors .= '<li><a href="#' . $trid . '">' . $sLang . ': key <em>' . $sKey . '</em> does not exist.</a></li>'; + $sTable .= '<td class="error">!!! MISS !!!</td>'; } } - $sTable.='</tr>'; + $sTable .= '</tr>'; } - $sTable.='</tbody></table>'; - if ($sWarnings)$sWarnings='<ol class="warning">'.$sWarnings.'</ol>'; - if ($sErrors)$sErrors='<ol class="error">'.$sErrors.'</ol>'; - $sOut.=$sErrors.$sWarnings.$sTable; + $sTable .= '</tbody></table>'; + if ($sWarnings) + $sWarnings = '<ol class="warning">' . $sWarnings . '</ol>'; + if ($sErrors) + $sErrors = '<ol class="error">' . $sErrors . '</ol>'; + $sOut .= $sErrors . $sWarnings . $sTable; } // ------------------------------------------------------------ // logoanalyzer // ------------------------------------------------------------ - if ($aParams["par3"]=="actionlog") { + if ($aParams["par3"] == "actionlog") { // $oPrj = new project(); - $TITLE=t("menu-logs"); + $TITLE = t("menu-logs"); require_once("./classes/actionlog.class.php"); - $oLog=new Actionlog(''); - - $sOut.=$oLog->renderLogs(array(), true); - + $oLog = new Actionlog(''); + + $sOut .= $oLog->renderLogs([], true); + } // ------------------------------------------------------------ // stats // ------------------------------------------------------------ // - if ($aParams["par3"]=="stats") { + if ($aParams["par3"] == "stats") { // $oPrj = new project(); require_once("./classes/actionlog.class.php"); - $oLog=new Actionlog(''); - - $sOut.='TODO' - //. $oLog->renderStats(array(), true) - ; - + $oLog = new Actionlog(''); + + $sOut .= 'TODO' + //. $oLog->renderStats([], true) + ; + } - + } - + } else { // ------------------------------------------------------------ @@ -354,21 +356,22 @@ if ($aParams["prj"] == "all") { */ if (array_key_exists("setupaction", $aParams) && $aParams["setupaction"] == "save") { if ($oPrj->saveConfig()) { - $sOut.=$oHtml->getBox("success", t("page-setup-info-settings-were-saved")); + $sOut .= $oHtml->getBox("success", t("page-setup-info-settings-were-saved")); } else { - $sOut.=$oHtml->getBox("error", t("page-setup-error-settings-were-not-saved")); + $sOut .= $oHtml->getBox("error", t("page-setup-error-settings-were-not-saved")); } } - $sErrors=$oPrj->renderErrorBoxes(); - $sOut.= $sErrors ? $sErrors : $oPrj->renderProjectSetup(); - $BODY= + $sErrors = $oPrj->renderErrorBoxes(); + $sOut .= $sErrors ? $sErrors : $oPrj->renderProjectSetup(); + $BODY = $renderAdminLTE->addRow( $renderAdminLTE->addCol( $renderAdminLTE->getCard([ - 'type'=>'primary', - 'variant'=>'outline', - 'text'=>$sOut, - ]), 12 + 'type' => 'primary', + 'variant' => 'outline', + 'text' => $sOut, + ]), + 12 ) ); diff --git a/public_html/deployment/pages/act_valuestore.php b/public_html/deployment/pages/act_valuestore.php index 3a35881fa7aa8a92a5d1f5c4d574115d1214acd3..df3c733b67f1107f4a4219041a4d780746e73357 100644 --- a/public_html/deployment/pages/act_valuestore.php +++ b/public_html/deployment/pages/act_valuestore.php @@ -6,38 +6,42 @@ VALUESTORE + TODO: use queryparam.class.php + --------------------------------------------------------------------- 2023-11-24 Axel <axel.hahn@unibe.ch> + ... + 2024-09-03 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax ###################################################################### */ -$TITLE=t('menu-valuestore'); +$TITLE = t('menu-valuestore'); // ---------------------------------------------------------------------- // functions // ---------------------------------------------------------------------- /** - * show an error message and quit with http status code 400 (Bad request) + * Show an error message and quit with http status code 400 (Bad request) * @param string $sMessage message to show - * @return boolean + * @return void */ -function quit($sMessage) +function quit(string $sMessage): void { $sProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); header("$sProtocol 400: Bad request"); - die("<h1>Bad request</h1>" . $sMessage); - return false; + die("<h1>Bad request</h1>$sMessage"); } /** - * get a request param from GET and POST scope (POST has priority) and + * Get a request param from GET and POST scope (POST has priority) and * verify it with execution of a cleanup array + * * @param string $sKey key to search for in GET or POST * @param string $sRegex4Cleanup regex for filtering - * @return type + * @return bool|string */ -function getParam($sKey, $sRegex4Cleanup = false) +function getParam(string $sKey, string $sRegex4Cleanup = ''): bool|string { $sValue = false; if (array_key_exists($sKey, $_GET)) { @@ -65,7 +69,7 @@ if (!$_GET || !count($_GET)) { quit("no parameter was found."); } -foreach (array("action", "project") as $sKey) { +foreach (["action", "project"] as $sKey) { if (!array_key_exists($sKey, $_GET)) { quit("value required: $sKey="); } @@ -100,8 +104,8 @@ $sValue = getParam('value', ''); $sOut = ''; $sHeader = ''; $sOut .= '' - .'<p>'.t('page-valuestore-hint').'</p>' - ; + . '<p>' . t('page-valuestore-hint') . '</p>' +; require './../valuestore/classes/valuestore.class.php'; $oVersion = new valuestore(); @@ -122,7 +126,7 @@ if (is_array($aData) && count($aData)) { // $sTable.='<td class="'.$sKey.'"><a href="'.$sUrl.'">'.$sValue.'</a></td>'; $sOnclick = ''; $sLabel = strstr($sValue, '"') - ? '<small>'.htmlentities($sValue).'</small>' + ? '<small>' . htmlentities($sValue) . '</small>' : '<a href="#" onclick="$(\'#eFilter\').val(\'' . $sValue . '\');filterTable();" title="click to filter by [' . $sValue . ']">' . htmlentities($sValue) . '</a>'; $sTable .= '<td class="' . $sKey . '" ' . $sOnclick . '>' . $sLabel . '</td>' . "\n"; } diff --git a/public_html/deployment/plugins/build/tgz/build_tgz.php b/public_html/deployment/plugins/build/tgz/build_tgz.php index 743a570b348326cc4ffe2a5766e70769c7d1e686..5ca737d12e5c782356bc04259a1caa191ab51036 100644 --- a/public_html/deployment/plugins/build/tgz/build_tgz.php +++ b/public_html/deployment/plugins/build/tgz/build_tgz.php @@ -5,28 +5,33 @@ * Build plugin - TGZ * * @author <axel.hahn@iml.unibe.ch> + * + * 2024-09-02 Axel php8 only; added variable types; short array syntax */ -class build_tgz extends build_base { - +class build_tgz extends build_base +{ + /** * check requirements if the plugin could work * @return array */ - public function checkRequirements() { + public function checkRequirements(): array + { return [ 'which tar' ]; } - + /** * get an array with shell commands to execute * @return array */ - public function getBuildCommands(){ + public function getBuildCommands(): array + { return [ - 'cd "'. $this->getBuildDir(). '" && tar -czf "'. $this->getOutfile().'" .' + 'cd "' . $this->getBuildDir() . '" && tar -czf "' . $this->getOutfile() . '" .' ]; } - + } diff --git a/public_html/deployment/plugins/build/zip/build_zip.php b/public_html/deployment/plugins/build/zip/build_zip.php index 2fadc359a115da1f020de2bb93ee7f625db53226..a36fcee0eb3e855e885e88d2ee846be282d59cb7 100644 --- a/public_html/deployment/plugins/build/zip/build_zip.php +++ b/public_html/deployment/plugins/build/zip/build_zip.php @@ -5,19 +5,23 @@ * Build plugin - TGZ * * @author <axel.hahn@iml.unibe.ch> + * + * 2024-09-02 Axel php8 only; added variable types; short array syntax */ -class build_zip extends build_base { - +class build_zip extends build_base +{ + /** * check requirements if the plugin could work * @return array */ - public function checkRequirements() { + public function checkRequirements(): array + { return [ 'which zip' ]; } - + /** * get an array with shell commands to execute * used zip params: @@ -26,11 +30,12 @@ class build_zip extends build_base { * -r recurse into directories * @return array */ - public function getBuildCommands(){ + public function getBuildCommands(): array + { return [ - 'cd "'. $this->getBuildDir(). '" && zip -9qr "'. $this->getOutfile().'" .' + 'cd "' . $this->getBuildDir() . '" && zip -9qr "' . $this->getOutfile() . '" .' ]; } - + } diff --git a/public_html/deployment/plugins/rollout/awx/rollout_awx.php b/public_html/deployment/plugins/rollout/awx/rollout_awx.php index 5bb671355cb88fb4f93a3c59ecb482075be05902..5c791f2e7328cf9f7fdf990db0d3c84b5ee4421b 100644 --- a/public_html/deployment/plugins/rollout/awx/rollout_awx.php +++ b/public_html/deployment/plugins/rollout/awx/rollout_awx.php @@ -4,203 +4,234 @@ * * Rollout plugin - awx * - * Run an Https POST request to AWX + * Run an Https GET or POST request to AWX * * @author <axel.hahn@iml.unibe.ch> + * + * 2024-09-02 Axel php8 only; added variable types; short array syntax */ -class rollout_awx extends rollout_base { - +class rollout_awx extends rollout_base +{ + // url part for AWX API request to set count of results per page - protected $_sAwxApiPaging='&page_size=10000&page=1'; + protected string $_sAwxApiPaging = '&page_size=10000&page=1'; /** - * check requirements if the plugin could work + * Get an array with shell commands to check requirements if the plugin + * can work + * + * @return array */ - public function checkRequirements() { - // no specific checks needed ... always true - return true; + public function checkRequirements(): array + { + // no specific checks needed ... always empty + return []; } - + /** - * check access to a deploy target + * Get an array with shell commands to check access to a deploy target + * + * @return array */ - public function checkConnectionToTarget() { - // do nothing ... always true - return true; + public function checkConnectionToTarget(): array + { + // do nothing ... always empty + return []; } - + /** - * make an http get request and return the response body + * Make an http get request and return the response body * it is called by _makeRequest * $aRequest contains subkeys - * - url - * - method; one of GET|POST|PUT|DELETE - * - postdata; for POST only + * - url relative urr; part behind api base url + * - method one of GET|POST|PUT|DELETE + * - postdata for POST only + * - ignore-ssl-error flag: if true it willignores ssl verifiction (not recommended) + * - user, password authentication with "user:password" * - * @param array $aRequest arrayurl for Foreman API - * @return string + * @param array $aRequest array with request data + * @param integer $iTimeout timeout in seconds + * @return array ... with subkeys "header" and "body" - or "error" if something went wrong */ - protected function _httpRequest($aRequest=false, $iTimeout = 5) { + protected function _httpRequest(array $aRequest = [], int $iTimeout = 5): array + { if (!function_exists("curl_init")) { die("ERROR: PHP CURL module is not installed."); } - $aConfig=$this->getConfig(); - - - $ch = curl_init($aConfig['url'].$aRequest['url']); + $aConfig = $this->getConfig(); + + $sAwxApiUrl=$aConfig['url'] . $aRequest['url']; + // base url --^ ^-- relative url to api + + $ch = curl_init($sAwxApiUrl); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $aRequest['method']); - if ($this->_aRequest['method']==='POST'){ + if ($aRequest['method'] === 'POST') { curl_setopt($ch, CURLOPT_POSTFIELDS, $aRequest['postdata']); } - if ($aConfig['user']){ - curl_setopt($ch, CURLOPT_USERPWD, $aConfig['user'].':'.$aConfig['password']); + if ($aConfig['user']) { + curl_setopt($ch, CURLOPT_USERPWD, $aConfig['user'] . ':' . $aConfig['password']); } - if (isset($aConfig['ignore-ssl-error']) && $aConfig['ignore-ssl-error']){ + if (isset($aConfig['ignore-ssl-error']) && $aConfig['ignore-ssl-error']) { curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); } - + curl_setopt($ch, CURLOPT_TIMEOUT, $iTimeout); curl_setopt($ch, CURLOPT_USERAGENT, 'IML Deployment :: rollout plugin awx ' . __CLASS__); - curl_setopt($ch, CURLOPT_HEADER,1); - curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $res = curl_exec($ch); - - $aReturn=array('info'=>curl_getinfo($ch), 'error'=>curl_error($ch)); + if (!$res) { + $iErrorCode = curl_errno($ch); + $sErrorMsg = curl_error($ch); + curl_close($ch); + return [ + 'error' => "Failed to fetch $sAwxApiUrl - curl error #$iErrorCode: $sErrorMsg" + ]; + } + + $aReturn = ['info' => curl_getinfo($ch), 'error' => curl_error($ch)]; curl_close($ch); - - $sHeader=substr($res, 0, $aReturn['info']['header_size']); - $aReturn['header']=explode("\n", $sHeader); - $aReturn['body']=str_replace($sHeader, "", $res); + + $sHeader = substr($res, 0, $aReturn['info']['header_size']); + $aReturn['header'] = explode("\n", $sHeader); + $aReturn['body'] = str_replace($sHeader, "", $res); // print_r($aReturn); return $aReturn; } /** - * get AWX inventories and return them as array for select box - * [id] => array('value' => [ID], 'label' => [NAME] [ID]) - * @return array + * Get AWX inventories and return them as array for select box + * [id] => ['value' => <ID>, 'label' => <NAME> <ID>] + * It returns false if the status code in the response is not 200 + * + * @return bool|array */ - public function getAwxInventories(){ - $aResponse=$this->_httpRequest(array( - 'url'=>'/inventories/?order_by=name'.$this->_sAwxApiPaging, - 'method'=>'GET', - ) + public function getAwxInventories(): bool|array + { + $aResponse = $this->_httpRequest( + [ + 'url' => '/inventories/?order_by=name' . $this->_sAwxApiPaging, + 'method' => 'GET', + ] ); - if(!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code']!==200){ + if (!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code'] !== 200) { return false; } - $aData=json_decode($aResponse['body'], 1); - $aReturn=[]; - if (!$aData || !isset($aData['count'])){ - $aReturn[]=[ - 'value'=>false, - 'label'=>'!!! Access to awx api failed !!!' + $aData = json_decode($aResponse['body'], 1); + $aReturn = []; + if (!$aData || !isset($aData['count'])) { + $aReturn[] = [ + 'value' => false, + 'label' => '!!! Access to awx api failed !!!' ]; return $aReturn; } - if(count($aData['results']) < $aData['count']){ - $aReturn[]=[ - 'value'=>false, - 'label'=>'>>>>>>>>> WARNING: fetched ' . count($aData['results']) . ' of ' .$aData['count'] . ' items only' + if (count($aData['results']) < $aData['count']) { + $aReturn[] = [ + 'value' => false, + 'label' => '>>>>>>>>> WARNING: fetched ' . count($aData['results']) . ' of ' . $aData['count'] . ' items only' ]; } - foreach ($aData['results'] as $aItem){ - $aReturn[$aItem['id']]= [ - 'value'=>$aItem['id'], - 'label'=>$aItem['name'].' (id: '.$aItem['id'].')' + foreach ($aData['results'] as $aItem) { + $aReturn[$aItem['id']] = [ + 'value' => $aItem['id'], + 'label' => $aItem['name'] . ' (id: ' . $aItem['id'] . ')' ]; } - return $aReturn; + return $aReturn; } /** * get AWX Job Templates and return them as array for select box - * [id] => array('value' => [ID], 'label' => [PLAYBOOK] [ID]) - * @return array + * [id] => ['value' => [ID], 'label' => [PLAYBOOK] [ID]] + * @return bool|array */ - public function getAwxJobTemplates(){ - $aResponse=$this->_httpRequest(array( - 'url'=>'/job_templates/?order_by=name'.$this->_sAwxApiPaging, - 'method'=>'GET', - ) + public function getAwxJobTemplates() + { + $aResponse = $this->_httpRequest( + [ + 'url' => '/job_templates/?order_by=name' . $this->_sAwxApiPaging, + 'method' => 'GET', + ] ); - if(!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code']!==200){ + if (!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code'] !== 200) { return false; } - $aData=json_decode($aResponse['body'], 1); - $aReturn=[]; - if (!$aData || !isset($aData['count'])){ - $aReturn[]=[ - 'value'=>false, - 'label'=>'!!! Access to awx api failed !!!' + $aData = json_decode($aResponse['body'], 1); + $aReturn = []; + if (!$aData || !isset($aData['count'])) { + $aReturn[] = [ + 'value' => false, + 'label' => '!!! Access to awx api failed !!!' ]; return $aReturn; } - if(count($aData['results']) < $aData['count']){ - $aReturn[]=[ - 'value'=>false, - 'label'=>'>>>>>>>>> WARNING: fetched ' . count($aData['results']) . ' of ' .$aData['count'] . ' items only' + if (count($aData['results']) < $aData['count']) { + $aReturn[] = [ + 'value' => false, + 'label' => '>>>>>>>>> WARNING: fetched ' . count($aData['results']) . ' of ' . $aData['count'] . ' items only' ]; } - foreach ($aData['results'] as $aItem){ - $aReturn[$aItem['id']]= [ - 'value'=>$aItem['id'], - 'label'=>$aItem['name'].' (id: '.$aItem['id'].'; '.$aItem['playbook'].')' + foreach ($aData['results'] as $aItem) { + $aReturn[$aItem['id']] = [ + 'value' => $aItem['id'], + 'label' => $aItem['name'] . ' (id: ' . $aItem['id'] . '; ' . $aItem['playbook'] . ')' ]; } - return $aReturn; + return $aReturn; } - + /** - * get array with commands to execute to deploy a package + * Get an array with shell commands to execute for deployment of built file * * @param string $sPhase * @param boolean $bMask Flag for public output; if true then mask your secrets * @return array */ - public function getDeployCommands($sPhase, $bMask=false){ - $aReturn=array(); - $aConfig=$this->getConfig($sPhase, $bMask); - + public function getDeployCommands(string $sPhase, bool $bMask = false): array + { + $aReturn = []; + $aConfig = $this->getConfig($sPhase, $bMask); + // ----- Checks: - $sCmdChecks=''; - if(isset($aConfig['extravars']) && $aConfig['extravars']){ - $aTmp=json_decode($aConfig['extravars'], 1); - if (!$aTmp || !is_array($aTmp) || !count($aTmp) ){ - $sCmdChecks.='echo "ERROR: Value in extravars has wrong Syntax - this is no JSON: '.$aConfig['extravars'].'"; exit 1; '; + $sCmdChecks = ''; + if (isset($aConfig['extravars']) && $aConfig['extravars']) { + $aTmp = json_decode($aConfig['extravars'], 1); + if (!$aTmp || !is_array($aTmp) || !count($aTmp)) { + $sCmdChecks .= 'echo "ERROR: Value in extravars has wrong Syntax - this is no JSON: ' . $aConfig['extravars'] . '"; exit 1; '; } - $aConfig['extravars']=json_encode($aTmp); + $aConfig['extravars'] = json_encode($aTmp); } - - if(!isset($aConfig['inventory']) || !(int)$aConfig['inventory']){ - $sCmdChecks.='echo "ERROR: no awx inventory was given."; exit 1; '; + + if (!isset($aConfig['inventory']) || !(int) $aConfig['inventory']) { + $sCmdChecks .= 'echo "ERROR: no awx inventory was given."; exit 1; '; } - + // ----- Send variables having values only - $aBodyvars=array(); - foreach(['inventory'=>'inventory', 'limit'=>'limit', 'job_tags'=>'tags', 'extra_vars'=>'extravars'] as $sParam=>$sVarkey){ + $aBodyvars = []; + foreach (['inventory' => 'inventory', 'limit' => 'limit', 'job_tags' => 'tags', 'extra_vars' => 'extravars'] as $sParam => $sVarkey) { if (isset($aConfig[$sVarkey]) && $aConfig[$sVarkey]) { - $aBodyvars[$sParam]=$aConfig[$sVarkey]; + $aBodyvars[$sParam] = $aConfig[$sVarkey]; } } - $sAuth=($aConfig['user'] ? '--user '.$aConfig['user'].':'.$aConfig['password'] : ''); - $aReturn[]=$sCmdChecks . "curl -f -k -H 'Content-Type: application/json' -XPOST -d '". json_encode($aBodyvars, JSON_PRETTY_PRINT)."' $sAuth ".$aConfig['url']."/job_templates/".$aConfig['jobtemplate']."/launch/"; + $sAuth = ($aConfig['user'] ? '--user ' . $aConfig['user'] . ':' . $aConfig['password'] : ''); + $aReturn[] = $sCmdChecks . "curl -f -k -H 'Content-Type: application/json' -XPOST -d '" . json_encode($aBodyvars, JSON_PRETTY_PRINT) . "' $sAuth " . $aConfig['url'] . "/job_templates/" . $aConfig['jobtemplate'] . "/launch/"; return $aReturn; } - + } diff --git a/public_html/deployment/plugins/rollout/default/rollout_default.php b/public_html/deployment/plugins/rollout/default/rollout_default.php index dcb10bce8e79e9f33b871e5135ba92c42c6f8255..da7fc3fc9c91d23aa4ea5717516c96746dd33310 100644 --- a/public_html/deployment/plugins/rollout/default/rollout_default.php +++ b/public_html/deployment/plugins/rollout/default/rollout_default.php @@ -6,33 +6,44 @@ * no action * * @author <axel.hahn@iml.unibe.ch> + * + * 2024-09-02 Axel php8 only; added variable types; short array syntax */ -class rollout_default extends rollout_base { +class rollout_default extends rollout_base +{ /** - * check requirements if the plugin could work + * Get an array with shell commands to check requirements if the plugin + * can work + * + * @return array */ - public function checkRequirements(){ - // no specific checks needed ... always true - return true; + public function checkRequirements(): array + { + // no specific checks needed ... always empty + return []; } /** - * check access to a deploy target + * Get an array with shell commands to check access to a deploy target + * + * @return array */ - public function checkConnectionToTarget(){ - // do nothing ... always true - return true; + public function checkConnectionToTarget(): array + { + // do nothing ... always empty + return []; } /** - * get array with commands to execute to deploy a package + * Get an array with shell commands to execute for deployment of built file * * @param string $sPhase * @param boolean $bMask Flag for public output; if true then mask your secrets * @return array */ - public function getDeployCommands($sPhase, $bMask=false){ + public function getDeployCommands(string $sPhase, bool $bMask = false): array + { return [ 'echo "SKIP"' ]; @@ -44,17 +55,19 @@ class rollout_default extends rollout_base { * * @return string */ - public function renderFormdata4Project() { + public function renderFormdata4Project(): string + { return $this->_t('no-cfg'); } /** - * override general form renderer: show a single message that no - * configuration items exist - * + * override of form renderer: show configuration for a given phase + + * @param string $sPhase phaese; one of preview|stage|live * @return string */ - public function renderFormdata4Phase($sPhase) { + public function renderFormdata4Phase(string $sPhase): string + { return $this->_t('no-cfg'); - } + } } diff --git a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php index 012fe813de13bf416271e4fb693f5ac941797a92..27e6c82dac4ca2474e949c26ff5440b47e27868d 100644 --- a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php +++ b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php @@ -7,34 +7,44 @@ * Run a SSH command on remote targets * * @author <axel.hahn@iml.unibe.ch> + * + * 2024-09-02 Axel php8 only; added variable types; short array syntax */ class rollout_ssh extends rollout_base { /** - * check requirements if the plugin could work + * Get an array with shell commands to check requirements if the plugin + * can work + * + * @return array */ - public function checkRequirements() { - // no specific checks needed ... always true - return true; + public function checkRequirements(): array + { + // no specific checks needed ... always empty + return []; } /** - * check access to a deploy target + * Get an array with shell commands to check access to a deploy target + * + * @return array */ - public function checkConnectionToTarget() { - // do nothing ... always true - return true; + public function checkConnectionToTarget(): array + { + // do nothing ... always empty + return []; } /** - * get array with commands to execute to deploy a package + * Get an array with shell commands to execute for deployment of built file * - * @param string $sPhase phase + * @param string $sPhase * @param boolean $bMask Flag for public output; if true then mask your secrets * @return array */ - public function getDeployCommands($sPhase,$bMask=false){ - $aReturn=array(); + public function getDeployCommands(string $sPhase, bool $bMask = false): array + { + $aReturn=[]; $aConfig=$this->getConfig($sPhase); // loop over hosts and create shell commands for it diff --git a/public_html/deployment/plugins/shellcmd/load/plugin.php b/public_html/deployment/plugins/shellcmd/load/plugin.php index 1eb0a12ca3ffb413ac78bfd4ca50ea70d75f7325..ff5fec241249d35797a285b321bc9c0166a1d598 100644 --- a/public_html/deployment/plugins/shellcmd/load/plugin.php +++ b/public_html/deployment/plugins/shellcmd/load/plugin.php @@ -4,26 +4,30 @@ * SHELLCMD PLUGIN :: LOAD * * ---------------------------------------------------------------------- - * 2022-08-05 axel.hahn@iml.unibe.ch - * 2023-12-13 axel.hahn@unibe.ch minified + * Axel: axel.hahn@unibe.ch + * 2022-08-05 Axel initial version + * 2023-12-13 Axel minified + * 2024-09-02 Axel php8 only; added variable types; short array syntax */ -class shellcmd_load { +class shellcmd_load +{ /** * parse output and extract wanted values in section "data" * @return array */ - public function parsedata($aResult){ - $aTmp1=array_reverse(explode(',', $aResult['output'][0])); - $aResult['data']=[ - 'uptime'=>(isset($aTmp1[5]) + public function parsedata(array $aResult): array + { + $aTmp1 = array_reverse(explode(',', $aResult['output'][0])); + $aResult['data'] = [ + 'uptime' => (isset($aTmp1[5]) ? trim(substr($aTmp1[5], 10) . $aTmp1[4]) : trim(substr($aTmp1[4], 10)) - ).' h' + ) . ' h' , - 'users'=>trim(str_replace(' users', '', $aTmp1[3])), - 'load'=>trim(preg_replace('/^.*:/', '', $aTmp1[2])), - 'load5'=>trim($aTmp1[1]), - 'load15'=>trim($aTmp1[0]), + 'users' => trim(str_replace(' users', '', $aTmp1[3])), + 'load' => trim(preg_replace('/^.*:/', '', $aTmp1[2])), + 'load5' => trim($aTmp1[1]), + 'load15' => trim($aTmp1[0]), ]; return $aResult; } diff --git a/public_html/deployment/plugins/shellcmd/processes/plugin.php b/public_html/deployment/plugins/shellcmd/processes/plugin.php index ef858fea181bb5731966db5839ad7be0776b8f52..ac6f0995883989902b9f0189c7ea3c2c4bb34762 100644 --- a/public_html/deployment/plugins/shellcmd/processes/plugin.php +++ b/public_html/deployment/plugins/shellcmd/processes/plugin.php @@ -4,17 +4,21 @@ * SHELLCMD PLUGIN :: Processes * * ---------------------------------------------------------------------- - * 2022-09-19 axel.hahn@iml.unibe.ch - * 2023-12-13 axel.hahn@unibe.ch minified + * Axel: axel.hahn@unibe.ch + * 2022-09-19 Axel initial version + * 2023-12-13 Axel minified + * 2024-09-02 Axel php8 only; added variable types; short array syntax */ -class shellcmd_processes { +class shellcmd_processes +{ /** * parse output and extract wanted values in section "data" * @return array */ - public function parsedata($aResult){ - $aResult['data']=[ - 'count'=>count($aResult['output'])-1, + public function parsedata(array $aResult): array + { + $aResult['data'] = [ + 'count' => count($aResult['output']) - 1, ]; return $aResult; } diff --git a/public_html/deployment/plugins/shellcmd/top/plugin.php b/public_html/deployment/plugins/shellcmd/top/plugin.php index b0a5fa562b6c8a942c961017a9b00ad541469ac3..a9c20409bed8b96b0ec4889ac48d7bf2491c0fc0 100644 --- a/public_html/deployment/plugins/shellcmd/top/plugin.php +++ b/public_html/deployment/plugins/shellcmd/top/plugin.php @@ -4,17 +4,21 @@ * SHELLCMD PLUGIN :: top * * ---------------------------------------------------------------------- - * 2023-11-23 axel.hahn@unibe.ch - * 2023-12-13 axel.hahn@unibe.ch minified + * Axel: axel.hahn@unibe.ch + * 2023-11-23 Axel initial version + * 2023-12-13 Axel minified + * 2024-09-02 Axel php8 only; added variable types; short array syntax */ -class shellcmd_top { +class shellcmd_top +{ /** * parse output and extract wanted values in section "data" * @return array */ - public function parsedata($aResult){ - $aResult['data']=[ - 'count'=>count($aResult['output'])-1, + public function parsedata(array $aResult): array + { + $aResult['data'] = [ + 'count' => count($aResult['output']) - 1, ]; return $aResult; } diff --git a/public_html/valuestore/browse.php b/public_html/valuestore/browse.php deleted file mode 100644 index f19da467f101fe776eb62d4062f8f8f0f81a793a..0000000000000000000000000000000000000000 --- a/public_html/valuestore/browse.php +++ /dev/null @@ -1,167 +0,0 @@ -<?php - -/** - * - * VERSION HANDLER API - * - * Syntax des Aufrufs (als GET Schreibweise; ein POST soll (später) auch gehen): - * http://dev.ci.iml.unibe.ch:8002/versions/?action=update&project=ci&phase=preview&host=ci.preview.iml.unibe.ch&variable=version&value=007 - * - * Parameter: - * - * action=update (get-Funktionen muss ich später noch bauen) - * project=[Projekt-ID (=Name des Package, das installiert wird)] - * phase=(preview|stage|live) - * host=[FQDN] - * variable=version (andere noch nicht unterstützt/ vorgesehen) - * value=[Versionsdaten] - * - * 2017-04-07 hahn first lines - */ - - -// ---------------------------------------------------------------------- -// functions -// ---------------------------------------------------------------------- - -/** - * show an error message and quit with http status code 400 (Bad request) - * @param string $sMessage message to show - * @return boolean - */ -function quit($sMessage) { - $sProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); - header("$sProtocol 400: Bad request"); - die("<h1>Bad request</h1>" . $sMessage); - return false; -} - -/** - * get a request param from GET and POST scope (POST has priority) and - * verify it with execution of a cleanup array - * @param string $sKey key to search for in GET or POST - * @param string $sRegex4Cleanup regex for filtering - * @return type - */ -function getParam($sKey, $sRegex4Cleanup = false) { - $sValue=false; - if (array_key_exists($sKey, $_GET)) { - $sValue=$_GET[$sKey]; - } - if (array_key_exists($sKey, $_POST)) { - $sValue=$_POST[$sKey]; - } - if(!$sValue){ - return false; - } - $sReturn = $sRegex4Cleanup ? preg_replace($sRegex4Cleanup, '', $sValue) : $sValue; - - if ($sReturn !== $sValue) { - quit("ERROR: the parameter value in [$sKey = ...] has a wrong format."); - } - return $sReturn; -} - -// ---------------------------------------------------------------------- -// check required params -// ---------------------------------------------------------------------- -/* -if (!$_GET || !count($_GET)) { - quit("no parameter was found."); -} - -foreach (array("action", "project") as $sKey) { - if (!array_key_exists($sKey, $_GET)) { - quit("value required: $sKey="); - } -} - */ - - -// ---------------------------------------------------------------------- -// get vars -// ---------------------------------------------------------------------- - - -$sAction = getParam('action', '/[^a-z]/'); - -$sProject = getParam('project', '/[^a-z\-0-9]/'); -$sPhase = getParam('phase', '/[^a-z]/'); -$sPlace = getParam('place', '/[^a-z]/'); -$sHost = getParam('host', '/[^a-z\.\-0-9]/'); - -if ($sHost && !$sPlace) { - $sPlace = 'installed'; -} - -$sVarname = getParam('variable', '/[^a-z]/'); -$sValue = getParam('value', ''); - - -// ---------------------------------------------------------------------- -// init class -// ---------------------------------------------------------------------- - -$sOut=''; -$sHeader=''; -$sOut.='<h1>data browser</h1>'; -require './classes/valuestore.class.php'; - -$oVersion = new valuestore(); - -$aData=$oVersion->dumpdb(); -$sTable=''; -$sTableHead=''; -if(is_array($aData) && count($aData)){ - // echo '<pre>' . print_r($aData, 1); - foreach ($aData as $aRow){ - $sItemUrl='index.php?action=show&project='.$aRow['project'].'&package='.$aRow['package'].'&phase='.$aRow['phase'].'&host='.$aRow['host'].'&variable='.$aRow['variable'].''; - $sTable.='<tr ondblclick="location.href=\''.$sItemUrl.'\'">'; - // $sTable.='<tr>'; - foreach ($aRow as $sKey=>$sValue){ - // $sTable.='<td class="'.$sKey.'"><a href="'.$sUrl.'">'.$sValue.'</a></td>'; - $sOnclick=''; - $sLabel=strstr($sValue, '"') - ? htmlentities($sValue) - : '<a href="#" onclick="$(\'#eFilter\').val(\''.$sValue.'\');filterTable();" title="click to filter by ['.$sValue.']">'.htmlentities($sValue).'</a>' - ; - $sTable.='<td class="'.$sKey.'" '.$sOnclick.'>'.$sLabel.'</td>'."\n"; - } - $sTable.='<td>' - // . '<button onclick="location.href=\''.$sItemUrl.'&action=get\'">View</button>' - . '<form method="POST" action="'.$sItemUrl.'">' - . '<input type="hidden" name="action" value="delete">' - . '<button>Delete</button>' - . '</form>' - . '</td>' - . '</tr>'."\n"; - - } - foreach (array_keys($aRow) as $sKey){ - $sTableHead.='<th class="'.$sKey.'">'.$sKey.'</th>'; - } - $sOut.='' - . '<form method="POST" action="index.php">' - . '<input type="hidden" name="action" value="cleanup">' - . '<button>Cleanup (older 1d)</button>' - . '</form><hr>' - - . '<table id="tbldata"><thead>' - . '<tr>'.$sTableHead. '<th>action</th></tr>' - . '</thead>' - . '<tbody>'.$sTable.'</tbody></table>'; - -} else { - $sOut.='no data yet.'; -} -// $sOut.='<script>addFilterToTable("tbldata"); filterTable();</script>'; - -require_once("../deployment/classes/page.class.php"); -$oPage = new Page(); -$oPage->addResponseHeader("Pragma: no-cache"); -$oPage->setOutputtype('html'); -$oPage->setHeader($sHeader); -$oPage->addJsOnReady('addFilterToTable(); filterTable();'); - -$oPage->setContent($sOut); -echo $oPage->render(); diff --git a/public_html/valuestore/classes/valuestore.class.php b/public_html/valuestore/classes/valuestore.class.php index 8110ba3824b292f61d758d71c6d59e530a441bdc..d31dc77f28e75c5d02e8630ffda14136c9f578bd 100644 --- a/public_html/valuestore/classes/valuestore.class.php +++ b/public_html/valuestore/classes/valuestore.class.php @@ -26,40 +26,84 @@ * $aVersions=$oVersion->deleteValues("version"); * * @author hahn + * + * Axel: <axel.hahn@unibe.ch> + * + * (...) + * 2024-09-04 Axel php8 only; added variable types; short array syntax */ -class valuestore { +class valuestore +{ + + /** + * Project id + * @var string + */ + public string $sProject = ''; + + /** + * Name of sPackage = project id in ci server + * @var string + */ + public string $sPackage = ''; + + /** + * Phase; one of preview|stage|live + * @var string + */ + public string $sPhase = ''; + + /** + * Name of place inside a phase; one of onhold|ready2install|deployed + * @var string + */ + public string $sPlace = ''; + + /** + * Name of the host + * @var string + */ + public string $sHost = ''; + + /** + * Name of the variable; eg. "version" + * @var string + */ + public string $sVariable = ''; - public $sProject = false; - public $sPackage = false; - public $sPhase = false; - public $sPlace = false; - public $sHost = false; - public $sVariable = false; - public $sData = false; - - protected $_bDebug = true; + /** + * Value of the variable + * @var string + */ + public string $sData = ''; /** - * filename of sqlite database file - * @var type + * Flag: enable debug? + * @var bool */ - private $_dbfile = false; + protected bool $_bDebug = true; /** - * database connection + * Filename of sqlite database file + * @var string + */ + private string $_dbfile = ''; + + /** + * Database connection * @var object */ - protected $_oDB = false; + protected object $_oDB; /** * TTL vor stored values - 1 day [sec] * @var integer */ - protected $_iTTL = 86400; + protected int $_iTTL = 86400; /** * create statement for the database - * @var type + * @var string */ private $_sCreate = ' CREATE TABLE "values" ( @@ -75,51 +119,71 @@ class valuestore { UNIQUE (project, package, phase, place, host, variable) ON CONFLICT REPLACE );' ; - - // hardcoded - protected $_allowedPhases=array('preview', 'stage', 'live'); - protected $_allowedPlaces=array('onhold', 'ready2install', 'deployed'); - + + /** + * List of allowed phases + * hardcoded :-/ + * + * @var array + */ + protected array $_allowedPhases = ['preview', 'stage', 'live']; + + /** + * List of allowed places + * hardcoded :-/ + * + * @var array + */ + + protected array $_allowedPlaces = ['onhold', 'ready2install', 'deployed']; + // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- - + /** - * constructor ... no params + * Constructor ... no params */ - public function __construct(){ - + public function __construct() + { + // cache dir is hardcoded to versions directory :-/ $this->_dbfile = __DIR__ . '/../data/versioncache.db'; - + $this->_oDB = new PDO("sqlite:" . $this->_dbfile); if (!file_exists($this->_dbfile) || !filesize($this->_dbfile)) { $this->_createDb(); } } + /** - * add a log messsage + * Add a logging messsage with ahlogger + * * @global object $oLog + * * @param string $sMessage messeage text * @param string $sLevel warnlevel of the given message * @return bool */ - private function log($sMessage, $sLevel = "info") { + private function log($sMessage, $sLevel = "info"): bool + { global $oCLog; - if($oCLog){ + if ($oCLog) { return $oCLog->add(basename(__FILE__) . " class " . __CLASS__ . " - " . $sMessage, $sLevel); } return false; } - + // ---------------------------------------------------------------------- // PRIVATE // ---------------------------------------------------------------------- /** * create sqlite database - called in constructor if the file does not exist + * @return boolean */ - private function _createDb() { + private function _createDb(): bool + { if (file_exists($this->_dbfile)) { echo $this->_bDebug ? "removing existing file $this->_dbfile ...<br>\n" : ''; unlink($this->_dbfile); @@ -129,116 +193,127 @@ class valuestore { $this->_oDB = new PDO("sqlite:" . $this->_dbfile); $this->_makeQuery($this->_sCreate); if (!file_exists($this->_dbfile)) { - $this->_quit(__FUNCTION__ , "ERROR: unable to create sqlite database " . $this->_dbfile); + $this->_quit(__FUNCTION__, "ERROR: unable to create sqlite database " . $this->_dbfile); } return true; } /** - * execute a sql statement + * Execute a sql statement + * It returns false if the query failed. + * On success you get a PDO result object + * * @param string $sSql sql statement * @param array $aVars array with values (uses PDO::prepare(); $sSql must contain placeholders :key) - * @return database object + * @return bool|object result object of query */ - private function _makeQuery($sSql, $aVars=false) { + private function _makeQuery(string $sSql, array $aVars = []): bool|object + { // $this->_log(__FUNCTION__."($sSql)"); // echo "DEBUG: executing SQL<pre>$sSql</pre>"; $this->log("start query"); - if ($aVars && is_array($aVars)){ + if ($aVars && is_array($aVars)) { $oStatement = $this->_oDB->prepare($sSql); $result = $oStatement->execute($aVars); } else { $result = $this->_oDB->query($sSql); } - if(!$result){ - $this->log('<pre>'.print_r($this->_oDB->errorInfo()).'</pre>', 'error'); + if (!$result) { + $this->log('<pre>' . print_r($this->_oDB->errorInfo()) . '</pre>', 'error'); } - $this->log("end query - ".$sSql); + $this->log("end query - " . $sSql); return $result; } - + /** - * execute a sql statement - * @param string $sSql sql statement + * Execute a sql SELECT statement and get an array with results + * + * @param string $sSql sql statement + * @param string $sKey optional: return only values of this key * @return array of resultset */ - private function _makeSelectQuery($sSql, $aKey=false) { + private function _makeSelectQuery(string $sSql, string $sKey = ''): array + { // $this->_log(__FUNCTION__."($sSql)"); // echo "DEBUG: executing select SQL<pre>$sSql</pre>"; $this->log("start query"); $oStatement = $this->_oDB->prepare($sSql); $oStatement->execute(); - $aReturn=array(); + $aReturn = []; while ($row = $oStatement->fetch(PDO::FETCH_ASSOC)) { - if ($aKey && array_key_exists($aKey, $row)){ - $aReturn[] = $row[$aKey]; - } else { - $aReturn[] = $row; - } - } - $this->log("end query - ".$sSql); + if ($sKey && array_key_exists($sKey, $row)) { + $aReturn[] = $row[$sKey]; + } else { + $aReturn[] = $row; + } + } + $this->log("end query - " . $sSql); return $aReturn; } /** - * log error and quit. it echoes the error message on screen if debug + * Log error and quit. it echoes the error message on screen if debug * is enabled. + * * @param string $sFunction name of method that throws the error * @param string $sMessage error message - * @return boolean + * @return void */ - private function _quit($sFunction, $sMessage){ + private function _quit(string $sFunction, string $sMessage): void + { error_log(__CLASS__ . "::$sFunction - $sMessage " . "whereiam: " . print_r($this->whereiam(), 1)); - if ($this->_bDebug){ + if ($this->_bDebug) { echo __CLASS__ . "::$sFunction stopped.<br>\n" - . "whereiam: <pre>" . print_r($this->whereiam(), 1)."</pre>" - ; + . "whereiam: <pre>" . print_r($this->whereiam(), 1) . "</pre>" + ; die($sMessage); } else { - die("ERROR ... wrong usage of class ". __CLASS__); + die("ERROR ... wrong usage of class " . __CLASS__); } - return false; } // ---------------------------------------------------------------------- // PUBLIC GETTER // ---------------------------------------------------------------------- - - + + /** - * get list of current projects - * @return type + * Get list of current projects + * @return array */ - public function getProjects(){ - $sSql="select distinct(project) from `values`"; + public function getProjects(): array + { + $sSql = "select distinct(project) from `values`"; return $this->_makeSelectQuery($sSql, 'project'); } - + /** - * get list of current projects - * @return type + * Get list of current projects + * @return array */ - public function getPackages(){ - $sSql="select distinct(package) from `values`"; + public function getPackages(): array + { + $sSql = "select distinct(package) from `values`"; return $this->_makeSelectQuery($sSql, 'package'); } - + /** - * get phases of the current project; a project must be set be set before - * @return type + * Get phases of the current project; a project must be set be set before + * @return array */ - public function getPhases(){ - if (!$this->sProject && !$this->sPackage){ - $this->_quit(__FUNCTION__ , "you need to set a project first. Use the setProject() method to do so."); + public function getPhases(): array + { + if (!$this->sProject && !$this->sPackage) { + $this->_quit(__FUNCTION__, "you need to set a project first. Use the setProject() method to do so."); } - $sWhere='1=1 ' - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - ; - $sSql="select distinct(phase) from `values` WHERE $sWhere"; - return $this->_makeSelectQuery($sSql,'phase'); + $sWhere = '1=1 ' + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + ; + $sSql = "select distinct(phase) from `values` WHERE $sWhere"; + return $this->_makeSelectQuery($sSql, 'phase'); } - + /** * get places of the current project; a project and must be set be set before * @return type @@ -259,254 +334,286 @@ class valuestore { return $this->_makeSelectQuery($sSql, 'place'); } */ - + /** * get hosts that have installed a project - * @return type + * @return array */ - public function getHosts(){ - $sWhere='1=1 ' - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") + public function getHosts(): array + { + $sWhere = '1=1 ' + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") . "AND place='deployed' " //. "AND host>'' " - ; - $sSql="select distinct(host) from `values` WHERE $sWhere"; + ; + $sSql = "select distinct(host) from `values` WHERE $sWhere"; return $this->_makeSelectQuery($sSql, 'host'); } /** - * get entries of the current place (project, phase and place can be + * Get entries of the current place (project, phase and place can be * set before) + * * @see setProject() + * * @param string $sVariable variable for filtering column "variable" * @return array */ - public function getVar($sVariable=''){ - $sWhere='1=1 ' - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") - . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") - . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") - . ($sVariable ? "AND variable='" . $sVariable . "' " : "") - ; - $sSql="select * from `values` WHERE $sWhere"; + public function getVar($sVariable = ''): array + { + $sWhere = '1=1 ' + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") + . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") + . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") + . ($sVariable ? "AND variable='" . $sVariable . "' " : "") + ; + $sSql = "select * from `values` WHERE $sWhere"; return $this->_makeSelectQuery($sSql); } - + /** - * get versions of the current place (project, phase and place can be + * Get versions of the current place (project, phase and place can be * set before) + * * @see setProject() + * * @return array */ - public function getVersion(){ - $aData=$this->getVar('version'); - + public function getVersion(): array + { + $aData = $this->getVar('version'); + // get json in subkey host -> data and store keys in host -> _data - if($aData && is_array($aData) && count($aData)){ - foreach($aData as $iKey=>$aHostinfos){ - $aJson=json_decode($aHostinfos['data'], 1); - $aData[$iKey]['_data']=$aJson; + if ($aData && is_array($aData) && count($aData)) { + foreach ($aData as $iKey => $aHostinfos) { + $aJson = json_decode($aHostinfos['data'], 1); + $aData[$iKey]['_data'] = $aJson; } } return $aData; } - + /** - * return currebntly set project, phase, place and host + * Get currently set project, phase, place and host * @return array */ - public function whereiam(){ - return array( - 'project'=>$this->sProject, - 'package'=>$this->sPackage, - 'phase'=>$this->sPhase, - 'place'=>$this->sPlace, - 'host'=>$this->sHost, - ); + public function whereiam(): array + { + return [ + 'project' => $this->sProject, + 'package' => $this->sPackage, + 'phase' => $this->sPhase, + 'place' => $this->sPlace, + 'host' => $this->sHost, + ]; } + /** - * return currebntly set project, phase, place and host - * @return type + * Get all values in valuestore as array + * @return array */ - public function dumpdb(){ - $sSql="select * from `values`"; + public function dumpdb(): array + { + $sSql = "select * from `values`"; return $this->_makeSelectQuery($sSql); } - - + + // ---------------------------------------------------------------------- // PUBLIC SETTER // ---------------------------------------------------------------------- - + /** - * init a "position" before getting or updating deleting a value + * Init a "position" before getting or updating deleting a value * * @param string $sProject project id * @param string $sPackage package id - * @param string $sPhase phase (preview|stage|live) - * @param string $sPlace place (onhold|ready2install|deployed) - * @param string $sHost hostname (for place deployed) - * @return type string + * @param string $sPhase optional: phase (preview|stage|live) + * @param string $sPlace optional: place (onhold|ready2install|deployed) + * @param string $sHost optional: hostname (for place deployed) + * @return string */ - public function setProject($sProject, $sPackage, $sPhase=false, $sPlace=false, $sHost=false){ - $this->sProject=preg_replace('/[^a-z\-\_0-9]/', '' ,$sProject); + public function setProject(string $sProject, string $sPackage, string $sPhase = '', string $sPlace = '', $sHost = ''): string + { + $this->sProject = preg_replace('/[^a-z\-\_0-9]/', '', $sProject); $this->setPackage($sPackage, $sPhase, $sPlace, $sHost); return $this->sProject; } /** - * set a package (and more granular items) + * Set a package (and more granular items) * @see setProject() * * @param string $sPackage package id * @param string $sPhase phase (preview|stage|live) - * @param string $sPlace place (onhold|ready2install|deployed) - * @param string $sHost hostname (for place deployed) - * @return type string + * @param string $sPlace optional: place (onhold|ready2install|deployed) + * @param string $sHost optional: hostname (for place deployed) + * @return string */ - public function setPackage($sPackage, $sPhase, $sPlace=false, $sHost=false){ - $this->sPackage=preg_replace('/[^a-z\-\_0-9]/', '' ,$sPackage); + public function setPackage(string $sPackage, string $sPhase, string $sPlace = '', string $sHost = ''): string + { + $this->sPackage = preg_replace('/[^a-z\-\_0-9]/', '', $sPackage); $this->setPhase($sPhase, $sPlace, $sHost); return $this->sPackage; } - + /** - * set a phase (and more granular items) + * Set a phase (and more granular items) * @see setProject() * * @param string $sPhase phase (preview|stage|live) - * @param string $sPlace place (onhold|ready2install|deployed) - * @param string $sHost hostname (for place deployed) - * @return type string + * @param string $sPlace optional: place (onhold|ready2install|deployed) + * @param string $sHost optional: hostname (for place deployed) + * @return string */ - public function setPhase($sPhase, $sPlace=false, $sHost=false){ - if (!$this->sProject && !$this->sPackage){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project. Use the setProject() method to do so."); + public function setPhase(string $sPhase, string $sPlace = '', string $sHost = ''): string + { + if (!$this->sProject && !$this->sPackage) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project. Use the setProject() method to do so."); return false; } - if ($sPhase && array_search($sPhase, $this->_allowedPhases)===false){ - $this->_quit(__FUNCTION__ , "ERROR: you set a wrong phase [$sPhase]"); + if ($sPhase && array_search($sPhase, $this->_allowedPhases) === false) { + $this->_quit(__FUNCTION__, "ERROR: you set a wrong phase [$sPhase]"); } - $this->sPhase=$sPhase; + $this->sPhase = $sPhase; $this->setPlace($sPlace, $sHost); return $this->sPhase; } /** - * set a place (and host) + * Set a place (and optional a host) + * It aborts if project and package are not set + * * @see setProject() * * @param string $sPlace place (onhold|ready2install|deployed) - * @param string $sHost hostname (for place deployed) - * @return type string + * @param string $sHost optional: hostname (for place deployed) + * @return bool */ - public function setPlace($sPlace, $sHost=false){ - if ((!$this->sProject && !$this->sPackage)){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project and phase. Use the setProject() method to do so."); + public function setPlace($sPlace, string $sHost = ''): bool + { + if ((!$this->sProject && !$this->sPackage)) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project and phase. Use the setProject() method to do so."); return false; } - if ($sPlace && !$this->sPhase){ - $this->_quit(__FUNCTION__ , "ERROR: you cannot set place [$sPlace] with leaving phase empty."); + if ($sPlace && !$this->sPhase) { + $this->_quit(__FUNCTION__, "ERROR: you cannot set place [$sPlace] with leaving phase empty."); } - if ($sPlace && array_search($sPlace, $this->_allowedPlaces)===false){ - $this->_quit(__FUNCTION__ , "ERROR: you set a wrong place [$sPlace]"); + if ($sPlace && array_search($sPlace, $this->_allowedPlaces) === false) { + $this->_quit(__FUNCTION__, "ERROR: you set a wrong place [$sPlace]"); } - $this->sPlace=$sPlace; + $this->sPlace = $sPlace; $this->setHost($sHost); - return $this->sPlace; + return true; } - + /** - * set a host for place "deployed" + * Set a host for place "deployed". + * It aborts if project and package are not set + * * @see setProject() * * @param string $sHost hostname - * @return type string + * @return bool */ - public function setHost($sHost=false){ - if ((!$this->sProject && !$this->sPackage)){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. Use the setProject() method to do so."); + public function setHost($sHost = ''): bool + { + if ((!$this->sProject && !$this->sPackage)) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. Use the setProject() method to do so."); return false; } - if($sHost && $this->sPlace!=='deployed'){ - $this->_quit(__FUNCTION__ , "ERROR: a host can be set on place [deployed] only."); + if ($sHost && $this->sPlace !== 'deployed') { + $this->_quit(__FUNCTION__, "ERROR: a host can be set on place [deployed] only."); } /* if(!$sHost && $this->sPlace==='deployed'){ $this->_quit(__FUNCTION__ , "ERROR: on place [deployed] a host MUST be set."); } */ - $this->sHost=preg_replace('/[^a-z\.\-0-9]/', '', $sHost); - return $this->sHost; + $this->sHost = preg_replace('/[^a-z\.\-0-9]/', '', $sHost); + return true; } - + // ---------------------------------------------------------------------- // DATABASE FUNCTIONS // ---------------------------------------------------------------------- - + /** - * cleanup value store + * Cleanup value store: delete entries older a given ttl value in seconds + * Afterwards a "vacuum" command will be started. + * * @param integer $iTtl optional: max age in seconds of items to keep; 0 to delete all; default is 1 day * @return boolean */ - public function cleanup($iTtl=false){ - if ($iTtl===false){ - $iTtl=$this->_iTTL; + public function cleanup(int $iTtl = -1): bool + { + if ($iTtl === -1) { + $iTtl = $this->_iTTL; } - $sTtlDate=date("Y-m-d H:i:s", date("U") - (int)$iTtl ); - $sSql="DELETE FROM `values` WHERE `time` < '$sTtlDate' " - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") - . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") - . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") - ; + $sTtlDate = date("Y-m-d H:i:s", date("U") - (int) $iTtl); + $sSql = "DELETE FROM `values` WHERE `time` < '$sTtlDate' " + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") + . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") + . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") + ; // die("$iTtl ... $sSql ... ABORT"); $this->_makeQuery($sSql); $this->_makeQuery("vacuum;"); return true; } - + /** - * delete values from value store. You need to call setProject() to set + * Delete values from value store. You need to call setProject() to set * project or package and optional phase, place, host. Then call this delete * method. + * It returns a PDO result object. + * It aborts if project and package are not set + * It returns false on database query error + * * @param string $sVariable optional: limit deletion to a given variable - * @return boolean + * @return boolean|object */ - public function deleteValues($sVariable){ - if ((!$this->sProject && !$this->sPackage) ){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. use the setProject() method to do so."); + public function deleteValues($sVariable): bool|object + { + if ((!$this->sProject && !$this->sPackage)) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. use the setProject() method to do so."); } - $sWhere='1=1 ' - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") - . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") - . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") - . ($sVariable ? "AND variable='" . $sVariable . "' " : "") - ; - $sSql="DELETE FROM `values` WHERE $sWhere"; + $sWhere = '1=1 ' + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") + . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") + . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") + . ($sVariable ? "AND variable='" . $sVariable . "' " : "") + ; + $sSql = "DELETE FROM `values` WHERE $sWhere"; // echo $sSql; // return true; return $this->_makeQuery($sSql); } - + /** - * update a version - * @return boolean + * Update a version informatio in value store. + * It returns a PDO result object. + * It aborts if project and package are not set + * It returns false on database query error + * + * @return boolean|object */ - public function updateVar($sVarname,$sValue){ - if ((!$this->sProject && !$this->sPackage) || !$this->sPhase || !$this->sPlace ){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. use the setProject() method to do so."); + public function updateVar($sVarname, $sValue): bool|object + { + if ((!$this->sProject && !$this->sPackage) || !$this->sPhase || !$this->sPlace) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. use the setProject() method to do so."); } $this->cleanup(); - $sSql=" + $sSql = " INSERT into `values` (`time`, `project`, `package`, `phase`, `place`, `host`, `variable`, `data` ) VALUES ( '" . date("Y-m-d H:i:s") . "', @@ -520,20 +627,25 @@ class valuestore { ); "; return $this->_makeQuery( - $sSql, - array( - 'variable'=>$sVarname, - 'value'=>$sValue, - ) + $sSql, + [ + 'variable' => $sVarname, + 'value' => $sValue, + ] ); } /** - * update a version value - * @param type $sVersioninfos - * @return boolean + * update a version value. It returns the return value of updateVar(). + * It returns a PDO result object. + * It aborts if project and package are not set + * It returns false on database query error + * + * @param string $sVersioninfos json of ci build data + * @return boolean|object */ - public function updateVersion($sVersioninfos){ + public function updateVersion($sVersioninfos): bool|object + { return $this->updateVar('version', $sVersioninfos); } - + } diff --git a/public_html/valuestore/index.php b/public_html/valuestore/index.php index 9da4fb06d263612a47028375867df729eb698cbe..f554bdbfc4c0b6d274457348a838f65df87cb4f2 100644 --- a/public_html/valuestore/index.php +++ b/public_html/valuestore/index.php @@ -4,8 +4,6 @@ * * VALUE HANDLER API * - * Syntax des Aufrufs (als GET Schreibweise; ein POST einzelner Variablen geht ebenso): - * * update * http://dev.ci.iml.unibe.ch:8002/valuestore/?action=update&package=player&phase=stage&place=deployed&host=stage01.player&variable=version&value=false * @@ -16,38 +14,49 @@ * * action=update (get-Funktionen muss ich später noch bauen) * project=[Projekt-ID (=Name des Package, das installiert wird); bei Versionen leer!] - * package=[Package-name] + * package=[Package-name] (Name des CI Projekts) * phase=(preview|stage|live) * host=[FQDN] * variable=version (andere noch nicht unterstützt/ vorgesehen) * value=[Versionsdaten] * * 2017-04-07 hahn first lines + * ... + * 2024-09-04 Axel <axel.hahn@unibe.ch> php8 only; added variable types; short array syntax */ // ---------------------------------------------------------------------- // functions // ---------------------------------------------------------------------- /** - * show an error message and quit with http status code 400 (Bad request) + * Show an error message and quit with http status code 400 (Bad request) + * * @param string $sMessage message to show - * @return boolean + * @return void */ -function quit($sMessage) { +function quit(string $sMessage): void +{ $sProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); header("$sProtocol 400: Bad request"); - die("<h1>Bad request</h1>" . $sMessage); - return false; + die( + "<h1>Bad request</h1> + <p>$sMessage.</p> + <hr> + <button onclick=\"history.back();\">back</button>" + ); } /** - * get a request param from GET and POST scope (POST has priority) and - * verify it with execution of a cleanup array + * Get a request param from GET and POST scope (POST has priority) and + * verify it with execution of a cleanup array. + * It returns false if thegiven key doesn't exist as parameter + * * @param string $sKey key to search for in GET or POST * @param string $sRegex4Cleanup regex for filtering - * @return type + * @return bool|int|string */ -function getParam($sKey, $sRegex4Cleanup = false) { +function getParam(string $sKey, string $sRegex4Cleanup = ''): bool|int|string +{ $sValue = false; if (array_key_exists($sKey, $_GET)) { $sValue = $_GET[$sKey]; @@ -66,29 +75,42 @@ function getParam($sKey, $sRegex4Cleanup = false) { return $sReturn; } -// ---------------------------------------------------------------------- -// check required params -// ---------------------------------------------------------------------- -/* -if (!$_GET || !count($_GET)) { - quit("no parameter was found."); -} +/** + * Throw an error if a given GET parameter has a given value. + * + * The purpose of this function is to prevent certain actions + * (like deleting a value) from being triggered by an URL. + * + * @param string $sVar name of the GET parameter to check + * @param string $sValue value of the GET parameter to check + * @return void */ - +function disallowGet(string $sVar, string $sValue): void +{ + if (isset($_GET[$sVar]) && $_GET[$sVar] == $sValue) { + quit("ERROR: $sVar = $sValue is not allowed with GET."); + } +} // ---------------------------------------------------------------------- // get vars // ---------------------------------------------------------------------- $sAction = getParam('action', '/[^a-z]/'); -if ($sAction !== "cleanup" - && $sAction !== "delete" - && $sAction !== "get" - && $sAction !== "show" - && $sAction !== "update" - ) { +if ( + $sAction !== "cleanup" + && $sAction !== "delete" + && $sAction !== "get" + && $sAction !== "show" + && $sAction !== "update" +) { quit("action is unknown or not implemented yet."); } +disallowGet('action', 'cleanup'); +disallowGet('action', 'delete'); + +// used by Anasible to update the value ... I disable it for now to fix it in the near future +// disallowGet('action', 'update'); $sProject = getParam('project', '/[^a-z\-\_0-9]/'); $sPackage = getParam('package', '/[^a-z\-\_0-9]/'); @@ -127,13 +149,12 @@ switch ($sAction) { } break; - break; case 'get': $oVersion->setProject($sProject, $sPackage, $sPhase, $sPlace, $sHost); header('Content-Type: application/json'); echo json_encode($oVersion->getVersion()); + return true; - break; case 'update': $oVersion->setProject($sProject, $sPackage, $sPhase, $sPlace, $sHost); if (!$sVarname) { @@ -143,7 +164,7 @@ switch ($sAction) { // case 'other-varname': case 'version': if ($oVersion->updateVar($sVarname, $sValue)) { - echo "OK: $sVarname was updated.".PHP_EOL."new value: $sValue".PHP_EOL; + echo "OK: $sVarname was updated." . PHP_EOL . "new value: $sValue" . PHP_EOL; } else { echo "ERROR: update for $sVarname failed."; } @@ -156,19 +177,18 @@ switch ($sAction) { break; case 'show': $oVersion->setProject($sProject, $sPackage, $sPhase, $sPlace, $sHost); - - echo '<h1>package ['.$sPackage.'] on '.$sHost.'</h1>' - . 'Host: '.$sHost.'<br>' - . 'package: '.$sPackage.'<br>' - . 'phase: '.$sPhase.'<br>' - . 'place: '.$sPlace.'<br>' - . '<br>' - . '<button onclick="history.back();">back</button><br>' - . '<hr>' - . '<pre>'.json_encode($oVersion->getVersion(), JSON_PRETTY_PRINT).'</pre>' - . '<hr>' - . '<button onclick="history.back();">back</button>' - . ''; + + echo '<h1>package [' . $sPackage . '] on ' . $sHost . '</h1>' + . 'Host: ' . $sHost . '<br>' + . 'package: ' . $sPackage . '<br>' + . 'phase: ' . $sPhase . '<br>' + . 'place: ' . $sPlace . '<br>' + . 'varaible: ' . $sVarname . '<br>' + . '<br>' + . '<button onclick="history.back();">back</button><br>' + . '<hr>' + . '<pre>' . json_encode($oVersion->getVersion(), JSON_PRETTY_PRINT) . '</pre>' + . ''; break; @@ -177,3 +197,5 @@ switch ($sAction) { quit("ERROR: the action is not supported."); break; } + +echo "<hr><button onclick=\"history.back();\">back</button>"; \ No newline at end of file diff --git a/public_html/vendor/shooker/shooker.php b/public_html/vendor/shooker/shooker.php index b042bb7956478e32379fdc7e94cd9772da314b1e..8956f64a3f8092e7af7e400507c19aa8747b6d6f 100644 --- a/public_html/vendor/shooker/shooker.php +++ b/public_html/vendor/shooker/shooker.php @@ -160,5 +160,3 @@ class ShookerTrigger { array_push($this->actions, $fxn); } } - -?> \ No newline at end of file diff --git a/public_html/versions/classes/versions.class.php b/public_html/versions/classes/versions.class.php deleted file mode 100644 index c3da08da4423a581cffa778cdd080d00c1f85587..0000000000000000000000000000000000000000 --- a/public_html/versions/classes/versions.class.php +++ /dev/null @@ -1,311 +0,0 @@ -<?php -/** - * version data handler for the deployment tool - * - * @author hahn - */ -class versions { - //put your code here - - public $sProject = false; - public $sPhase = false; - public $sPlace = false; - public $sVariable = false; - public $sData = false; - - /** - * filename of sqlite database file - * @var type - */ - private $_dbfile = false; - - /** - * create statement for the database - * @var type - */ - private $_sCreate = ' - CREATE TABLE "versions" ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , - `time` DATETIME, - `project` TEXT, - `phase` TEXT, - `place` TEXT, - `host` TEXT, - `variable` TEXT, - `data` TEXT, - UNIQUE (project, phase, place, host, variable) ON CONFLICT REPLACE - );' - ; - - // ---------------------------------------------------------------------- - // CONSTRUCTOR - // ---------------------------------------------------------------------- - - /** - * constructor ... no params - */ - public function __construct(){ - - // cache dir is hardcoded to versions directory :-/ - $this->_dbfile = __DIR__ . '/../data/versioncache.db'; - - if (!file_exists($this->_dbfile)) { - $this->_createDb(); - } - } - - // ---------------------------------------------------------------------- - // PRIVATE - // ---------------------------------------------------------------------- - - /** - * create sqlite database - called in constructor if the file does not exist - */ - private function _createDb() { - if (file_exists($this->_dbfile)) { - echo "removing existing file $this->_dbfile ...<br>\n"; - unlink($this->_dbfile); - } - echo "create database as file $this->_dbfile ...<br>\n"; - $this->_makeQuery($this->_sCreate); - if (!file_exists($this->_dbfile)) { - die("ERROR: unable to create sqlite database " . $this->_dbfile); - } - return true; - } - - /** - * execute a sql statement - * @param string $sSql sql statement - * @param array $aVars array with values (uses PDO::prepare(); $sSql must contain placeholders :key) - * @return database object - */ - private function _makeQuery($sSql, $aVars=false) { - // $this->_log(__FUNCTION__."($sSql)"); - // echo "DEBUG: executing SQL<pre>$sSql</pre>"; - $oDb = new PDO("sqlite:" . $this->_dbfile); - if ($aVars && is_array($aVars)){ - $sth = $oDb->prepare($sSql); - $result = $sth->execute($aVars); - } else { - $result = $oDb->query($sSql); - } - return $result; - } - - /** - * execute a sql statement - * @param string $sSql sql statement - * @return database object - */ - private function _makeSelectQuery($sSql, $aKey=false) { - // $this->_log(__FUNCTION__."($sSql)"); - // echo "DEBUG: executing select SQL<pre>$sSql</pre>"; - $oDb = new PDO("sqlite:" . $this->_dbfile); - $oStatement = $oDb->prepare($sSql); - $oStatement->execute(); - $aReturn=array(); - while ($row = $oStatement->fetch(PDO::FETCH_ASSOC)) { - if ($aKey && array_key_exists($aKey, $row)){ - $aReturn[] = $row[$aKey]; - } else { - $aReturn[] = $row; - } - } - return $aReturn; - } - - - // ---------------------------------------------------------------------- - // PUBLIC GETTER - // ---------------------------------------------------------------------- - - - /** - * get list of current projects - * @return type - */ - public function getProjects(){ - $sSql="select distinct(project) from `versions`"; - return $this->_makeSelectQuery($sSql, 'project'); - } - - /** - * get phases of the current project; a project must be set be set before - * @return type - */ - public function getPhases(){ - if (!$this->sProject){ - die("ERROR: you need to set a project first. Use the setProject() method to do so."); - } - $sSql="select distinct(phase) from `versions` - WHERE - project='" . $this->sProject . "' - " - ; - return $this->_makeSelectQuery($sSql,'phase'); - } - - /** - * get places of the current project; a project and must be set be set before - * @return type - */ - public function getPlaces(){ - if (!$this->sProject || !$this->sPhase){ - die("ERROR: you need to set a project, and phase first. Use the setProject() method to do so."); - } - $sSql="select distinct(place) from `versions` - WHERE - project='" . $this->sProject . "' - AND phase='" . $this->sPhase . "' - "; - return $this->_makeSelectQuery($sSql, 'place'); - } - - /** - * get hosts that have installed a project - * @return type - */ - public function getHosts(){ - if (!$this->sProject || !$this->sPhase){ - die("ERROR: you need to set a project, and phase first. Use the setProject() method to do so."); - } - $sSql="select distinct(host) from `versions` - WHERE - project='" . $this->sProject . "' - AND phase='" . $this->sPhase . "' - AND place='deployed' - "; - return $this->_makeSelectQuery($sSql, 'host'); - } - - /** - * get versions of the current place (project, phase and place must be - * set before) - * @see setProject() - * @return type - */ - public function getVersion(){ - if (!$this->sProject || !$this->sPhase || !$this->sPlace ){ - die("ERROR: you need to set a project, phase and place first. Use the setProject() method to do so."); - } - $sSql="select data from `versions` - WHERE - project='" . $this->sProject . "' - AND phase='" . $this->sPhase . "' - AND place='" . $this->sPlace . "' - AND host='" . $this->sHost . "' - AND variable='version' - "; - return $this->_makeSelectQuery($sSql, 'data'); - } - - /** - * return currebntly set project, phase, place and host - * @return type - */ - public function whereiam(){ - return array( - 'project'=>$this->sProject, - 'phase'=>$this->sPhase, - 'place'=>$this->sPlace, - 'host'=>$this->sHost, - ); - } - /** - * return currebntly set project, phase, place and host - * @return type - */ - public function dumpdb(){ - $sSql="select * from `versions`"; - return $this->_makeSelectQuery($sSql); - } - - - // ---------------------------------------------------------------------- - // PUBLIC SETTER - // ---------------------------------------------------------------------- - - public function setProject($sProject, $sPhase=false, $sPlace=false, $sHost=false){ - $this->sProject=$sProject; - $this->setPhase($sPhase, $sPlace, $sHost); - return $this->sProject; - } - - public function setPhase($sPhase, $sPlace=false, $sHost=false){ - if (!$this->sProject){ - die("ERROR: you need to set a project. Use the setProject() method to do so."); - return false; - } - $this->sPhase=$sPhase; - $this->setPlace($sPlace, $sHost); - return $this->sPhase; - } - - public function setPlace($sPlace, $sHost=false){ - if (!$this->sProject || !$this->sPhase){ - die("ERROR: you need to set a project and phase. Use the setProject() method to do so."); - return false; - } - if($sPlace!=='onhold' && $sPlace!=='ready2install' && $sPlace!=='deployed'){ - die("ERROR: you set a wrong place [$sPlace]"); - } - $this->sPlace=$sPlace; - $this->setHost($sHost); - return $this->sPlace; - } - - public function setHost($sHost=false){ - if (!$this->sProject || !$this->sPhase || !$this->sPlace){ - die("ERROR: you need to set a project, phase and place. Use the setProject() method to do so."); - return false; - } - if($sHost && $this->sPlace!=='deployed'){ - die("ERROR: a host can be set on place [deployed] only."); - } - if(!$sHost && $this->sPlace==='deployed'){ - die("ERROR: on place [deployed] a host MUST be set."); - } - $this->sHost=$sHost; - return $this->sHost; - } - - /** - * update a version - * @return boolean - */ - public function updateVar($sVarname,$sValue){ - if (!$this->sProject || !$this->sPhase || !$this->sPlace ){ - die("ERROR: you need to set a project, phase and place. use the setProject() method to do so."); - } - $sSql=" - INSERT into `versions` - (`time`, `project`, `phase`, `place`, `host`, `variable`, `data` ) - VALUES - ( - '" . date("Y-m-d H:i:s") . "', - '" . $this->sProject . "', - '" . $this->sPhase . "', - '" . $this->sPlace . "', - '" . $this->sHost . "', - :variable, - :value - ); - "; - return $this->_makeQuery( - $sSql, - array( - 'variable'=>$sVarname, - 'value'=>$sValue, - ) - ); - } - /** - * update a version - * @return boolean - */ - public function updateVersion($sVersioninfos){ - return $this->updateVar('version', $sVersioninfos); - } - -} diff --git a/public_html/versions/data/.htkeep b/public_html/versions/data/.htkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/public_html/versions/index.php b/public_html/versions/index.php deleted file mode 100644 index 8fbb34621e34ff0726834e92b03f430b06a15d54..0000000000000000000000000000000000000000 --- a/public_html/versions/index.php +++ /dev/null @@ -1,140 +0,0 @@ -<?php - -/** - * - * VERSION HANDLER API - * - * Syntax des Aufrufs (als GET Schreibweise; ein POST soll (später) auch gehen): - * http://dev.ci.iml.unibe.ch:8002/versions/?action=update&project=ci&phase=preview&host=ci.preview.iml.unibe.ch&variable=version&value=007 - * - * Parameter: - * - * action=update (get-Funktionen muss ich später noch bauen) - * project=[Projekt-ID (=Name des Package, das installiert wird)] - * phase=(preview|stage|live) - * host=[FQDN] - * variable=version (andere noch nicht unterstützt/ vorgesehen) - * value=[Versionsdaten] - * - * 2017-04-07 hahn first lines - */ - - -// ---------------------------------------------------------------------- -// functions -// ---------------------------------------------------------------------- - -/** - * show an error message and quit with http status code 400 (Bad request) - * @param string $sMessage message to show - * @return boolean - */ -function quit($sMessage) { - $sProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); - header("$sProtocol 400: Bad request"); - die("<h1>Bad request</h1>" . $sMessage); - return false; -} - -/** - * get a request param from GET and POST scope (POST has priority) and - * verify it with execution of a cleanup array - * @param string $sKey key to search for in GET or POST - * @param string $sRegex4Cleanup regex for filtering - * @return type - */ -function getParam($sKey, $sRegex4Cleanup = false) { - $sValue=false; - if (array_key_exists($sKey, $_GET)) { - $sValue=$_GET[$sKey]; - } - if (array_key_exists($sKey, $_POST)) { - $sValue=$_POST[$sKey]; - } - if(!$sValue){ - return false; - } - $sReturn = $sRegex4Cleanup ? preg_replace($sRegex4Cleanup, '', $sValue) : $sValue; - - if ($sReturn !== $sValue) { - quit("ERROR: the parameter value in [$sKey = ...] has a wrong format."); - } - return $sReturn; -} - -// ---------------------------------------------------------------------- -// check required params -// ---------------------------------------------------------------------- -if (!$_GET || !count($_GET)) { - quit("no parameter was found."); -} - - -foreach (array("action", "project") as $sKey) { - if (!array_key_exists($sKey, $_GET)) { - quit("value required: $sKey="); - } -} - -// ---------------------------------------------------------------------- -// get vars -// ---------------------------------------------------------------------- - - -$sAction = getParam('action', '/[^a-z]/'); -if ($sAction !== "get" && $sAction !== "update") { - quit("action is unknown or not implemented yet."); -} - -$sProject = getParam('project', '/[^a-z\-0-9]/'); -$sPhase = getParam('phase', '/[^a-z]/'); -$sPlace = getParam('place', '/[^a-z]/'); -$sHost = getParam('host', '/[^a-z\.\-0-9]/'); - -if ($sHost && !$sPlace) { - $sPlace = 'deployed'; -} - -$sVarname = getParam('variable', '/[^a-z]/'); -$sValue = getParam('value', ''); - - -// ---------------------------------------------------------------------- -// init class -// ---------------------------------------------------------------------- - -require './classes/versions.class.php'; - -$oVersion = new versions(); -$oVersion->setProject($sProject, $sPhase, $sPlace, $sHost); - -switch ($sAction) { - case 'get': - print_r($oVersion->getVersion()); - - break; - case 'update': - if (!$sVarname) { - quit("ERROR: the update action requires a variable and a value."); - } - switch ($sVarname) { - // case 'other-varname': - case 'version': - if ($oVersion->updateVar($sVarname, $sValue)){ - echo "OK: $sVarname was updated."; - } else { - echo "ERROR: update for $sVarname failed."; - } - break; - - default: - quit("ERROR: update of variable [$sVarname] is not supported."); - break; - } - break; - - - default: - quit("ERROR: the action is not supported."); - break; -}