parent
commit
52327da82c
Firmato da: g.catania
SSH Key Fingerprint: SHA256:g+0mpeM5Sao/MuU1CjmosgrnDQg6K36kk0o511yiico
  1. 3
      README.md
  2. 12
      idrocap_wa/.htaccess
  3. 20
      idrocap_wa/Dockerfile
  4. 61
      idrocap_wa/README.md
  5. 47
      idrocap_wa/bin/bash_completion.sh
  6. 75
      idrocap_wa/bin/cake
  7. 27
      idrocap_wa/bin/cake.bat
  8. 10
      idrocap_wa/bin/cake.php
  9. 67
      idrocap_wa/composer.json
  10. 7466
      idrocap_wa/composer.lock
  11. 38
      idrocap_wa/config/.env.example
  12. 436
      idrocap_wa/config/app.php
  13. 94
      idrocap_wa/config/app_local.example.php
  14. 94
      idrocap_wa/config/app_local.php
  15. 231
      idrocap_wa/config/bootstrap.php
  16. 35
      idrocap_wa/config/bootstrap_cli.php
  17. 43
      idrocap_wa/config/cakelte.php
  18. 52
      idrocap_wa/config/keys/private.key
  19. 15
      idrocap_wa/config/keys/public.key
  20. 94
      idrocap_wa/config/paths.php
  21. 15
      idrocap_wa/config/plugins.php
  22. 357
      idrocap_wa/config/routes.php
  23. 18
      idrocap_wa/config/schema/i18n.sql
  24. 15
      idrocap_wa/config/schema/sessions.sql
  25. 152
      idrocap_wa/dockerfiles/cake
  26. 93
      idrocap_wa/dockerfiles/run
  27. 453
      idrocap_wa/dockerfiles/www.conf
  28. 16
      idrocap_wa/index.php
  29. 39
      idrocap_wa/jixel-background-task-handler/Dockerfile
  30. 152
      idrocap_wa/jixel-background-task-handler/jbth.py
  31. 9
      idrocap_wa/jixel-background-task-handler/jbth.sh
  32. 169
      idrocap_wa/jixel-background-task-handler/jgprc.py
  33. 26
      idrocap_wa/jixel-background-task-handler/jgprc.sh
  34. 12
      idrocap_wa/jixel-background-task-handler/jixel-background-task-handler.sh
  35. 0
      idrocap_wa/jixel-background-task-handler/jixel-cron.log
  36. 5
      idrocap_wa/jixel-background-task-handler/jixel-cron.sh
  37. 6
      idrocap_wa/jixel-background-task-handler/jixel-cron.txt
  38. 12
      idrocap_wa/jixel-background-task-handler/jixel-general-purpose-rabbit-consumer.sh
  39. 12
      idrocap_wa/phpcs.xml
  40. 6
      idrocap_wa/phpstan.neon
  41. 37
      idrocap_wa/phpunit.xml.dist
  42. 0
      idrocap_wa/plugins/.gitkeep
  43. 15
      idrocap_wa/psalm.xml
  44. 0
      idrocap_wa/resources/.gitkeep
  45. 279
      idrocap_wa/resources/locales/cake.pot
  46. 1709
      idrocap_wa/resources/locales/default.pot
  47. 1709
      idrocap_wa/resources/locales/en/default.po
  48. 1709
      idrocap_wa/resources/locales/en/previous_default.po
  49. 279
      idrocap_wa/resources/locales/it/cake.po
  50. 268
      idrocap_wa/src/Application.php
  51. 95
      idrocap_wa/src/Command/AddCadastralCodeToUsesCommand.php
  52. 44
      idrocap_wa/src/Command/AddDerivationCoordinatesCommand.php
  53. 57
      idrocap_wa/src/Command/CheckExpiredWaterDrawingAntimafiaCertificateRequestsCommand.php
  54. 144
      idrocap_wa/src/Command/CopyTranslationsCommand.php
  55. 107
      idrocap_wa/src/Command/IntendedUseVegetationCheckCommand.php
  56. 65
      idrocap_wa/src/Command/NormalizeApplicantProvinceCommand.php
  57. 65
      idrocap_wa/src/Command/NotificationsHandlerCommand.php
  58. 98
      idrocap_wa/src/Command/UnpackWaterDrawingPaperworkSnapshotsCommand.php
  59. 250
      idrocap_wa/src/Console/Installer.php
  60. 91
      idrocap_wa/src/Controller/AiServicesController.php
  61. 74
      idrocap_wa/src/Controller/Api/AppController.php
  62. 53
      idrocap_wa/src/Controller/Api/AttachmentsController.php
  63. 70
      idrocap_wa/src/Controller/Api/ErrorController.php
  64. 156
      idrocap_wa/src/Controller/Api/NotificationsController.php
  65. 31
      idrocap_wa/src/Controller/Api/OrganisationTypesController.php
  66. 157
      idrocap_wa/src/Controller/Api/OrganisationsController.php
  67. 67
      idrocap_wa/src/Controller/Api/SubscriptionsController.php
  68. 230
      idrocap_wa/src/Controller/Api/UsersController.php
  69. 75
      idrocap_wa/src/Controller/Api/WaterDrawingDerivationsController.php
  70. 47
      idrocap_wa/src/Controller/Api/WaterDrawingMeasurementsController.php
  71. 170
      idrocap_wa/src/Controller/Api/WaterDrawingPaperworksController.php
  72. 424
      idrocap_wa/src/Controller/AppController.php
  73. 112
      idrocap_wa/src/Controller/ApplicantsController.php
  74. 98
      idrocap_wa/src/Controller/AttachmentsController.php
  75. 205
      idrocap_wa/src/Controller/CapabilitiesController.php
  76. 0
      idrocap_wa/src/Controller/Component/.gitkeep
  77. 103
      idrocap_wa/src/Controller/Component/StreamableCsvExportTrait.php
  78. 56
      idrocap_wa/src/Controller/ControllableObjectsController.php
  79. 35
      idrocap_wa/src/Controller/DashboardController.php
  80. 185
      idrocap_wa/src/Controller/DeliveriesController.php
  81. 66
      idrocap_wa/src/Controller/DistrictsController.php
  82. 78
      idrocap_wa/src/Controller/EmailsController.php
  83. 70
      idrocap_wa/src/Controller/ErrorController.php
  84. 78
      idrocap_wa/src/Controller/FaxesController.php
  85. 25
      idrocap_wa/src/Controller/FiltersController.php
  86. 236
      idrocap_wa/src/Controller/GroupsController.php
  87. 187
      idrocap_wa/src/Controller/MapsController.php
  88. 79
      idrocap_wa/src/Controller/MobilePhonesController.php
  89. 97
      idrocap_wa/src/Controller/NotificationsController.php
  90. 204
      idrocap_wa/src/Controller/OrganisationsController.php
  91. 78
      idrocap_wa/src/Controller/PecsController.php
  92. 78
      idrocap_wa/src/Controller/PhonesController.php
  93. 174
      idrocap_wa/src/Controller/PrivacyController.php
  94. 91
      idrocap_wa/src/Controller/TagsController.php
  95. 76
      idrocap_wa/src/Controller/TelegramChatsController.php
  96. 624
      idrocap_wa/src/Controller/UsersController.php
  97. 124
      idrocap_wa/src/Controller/WaterDrawingArticlesController.php
  98. 123
      idrocap_wa/src/Controller/WaterDrawingDerivationsController.php
  99. 174
      idrocap_wa/src/Controller/WaterDrawingFeesController.php
  100. 108
      idrocap_wa/src/Controller/WaterDrawingIntendedUsesController.php
  101. Some files were not shown because too many files have changed in this diff Show More

3
README.md

@ -1 +1,2 @@
test
idrocap_wa -> Web Application

12
idrocap_wa/.htaccess

@ -0,0 +1,12 @@
# Uncomment the following to prevent the httpoxy vulnerability
# See: https://httpoxy.org/
#<IfModule mod_headers.c>
# RequestHeader unset Proxy
#</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^(\.well-known/.*)$ $1 [L]
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
</IfModule>

20
idrocap_wa/Dockerfile

@ -0,0 +1,20 @@
FROM registrywgs.webgenesis.it/webgenesys/jixel/jixel-dockers/almalinux9.5-php8.4.4:latest-master
RUN dnf -y install npm
RUN npm install -g bower
RUN dnf -y install git
RUN mkdir /var/www/jixel
COPY . /var/www/jixel
WORKDIR /var/www/jixel
RUN composer install --no-interaction
WORKDIR /var/www/jixel/webroot
RUN npm install
RUN bower install
EXPOSE 443
EXPOSE 80
WORKDIR /var/www/jixel
RUN mkdir /run/php-fpm
COPY ./dockerfiles/run /usr/local/bin/run
RUN chmod 500 /usr/local/bin/run
COPY ./dockerfiles/cake /var/www/jixel/bin/cake
RUN chmod 500 /var/www/jixel/bin/cake
CMD ["run"]

61
idrocap_wa/README.md

@ -0,0 +1,61 @@
# IDROCAP Jixel repository
Jixel core for IDROCAP developing. Based on [CakePHP](https://cakephp.org) 5.x.
The framework source code can be found here: [cakephp/cakephp](https://github.com/cakephp/cakephp).
Before you run the docker stack:
make sure you have a few configs keys under in `config/app_local.php`:
```php
'Custom' => [
'background_tasks' => [
'notifications' => [
'channel' => 'newjixel_background_tasks',
]
],
],
```
and:
```php
'Datasources' => [
'default' => [
'host' => 'mysql8',
'username' => 'jixel',
'password' => 'jixel2023',
'database' => 'jixel',
'url' => env('DATABASE_URL', null),
],
'geo' => [
'host' => 'mysql-geo',
'port' => '3307',
'username' => 'gecos',
'password' => 'gecos2015',
'database' => 'geo',
],
'test' => [
'host' => 'localhost',
'username' => 'my_app',
'password' => 'secret',
'database' => 'test_myapp',
'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tests.sqlite'),
],
],
````
First bootstrap only (and only if you are mapping in your host machine, idrocapjixel repository):
enter the container:
`docker exec -it -u <USERNAME> jixel`
install vendor packages:
`composer install -n`
install vendor front end packages:
`cd webroot`
`npm install -y`
`bower install -y`

47
idrocap_wa/bin/bash_completion.sh

@ -0,0 +1,47 @@
#
# Bash completion file for CakePHP console.
# Copy this file to a file named `cake` under `/etc/bash_completion.d/`.
# For more info check https://book.cakephp.org/4/en/console-commands/completion.html#how-to-enable-bash-autocompletion-for-the-cakephp-console
#
_cake()
{
local cur prev opts cake
COMPREPLY=()
cake="${COMP_WORDS[0]}"
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ "$cur" == -* ]] ; then
if [[ ${COMP_CWORD} = 1 ]] ; then
opts=$(${cake} completion options)
elif [[ ${COMP_CWORD} = 2 ]] ; then
opts=$(${cake} completion options "${COMP_WORDS[1]}")
else
opts=$(${cake} completion options "${COMP_WORDS[1]}" "${COMP_WORDS[2]}")
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
if [[ ${COMP_CWORD} = 1 ]] ; then
opts=$(${cake} completion commands)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
if [[ ${COMP_CWORD} = 2 ]] ; then
opts=$(${cake} completion subcommands $prev)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
if [[ $COMPREPLY = "" ]] ; then
_filedir
return 0
fi
return 0
fi
return 0
}
complete -F _cake cake bin/cake

75
idrocap_wa/bin/cake

@ -0,0 +1,75 @@
#!/usr/bin/env sh
################################################################################
#
# Cake is a shell script for invoking CakePHP shell commands
#
# CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
#
# @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
# @link https://cakephp.org CakePHP(tm) Project
# @since 1.2.0
# @license https://opensource.org/licenses/mit-license.php MIT License
#
################################################################################
# Canonicalize by following every symlink of the given name recursively
canonicalize() {
NAME="$1"
if [ -f "$NAME" ]
then
DIR=$(dirname -- "$NAME")
NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME")
fi
while [ -h "$NAME" ]; do
DIR=$(dirname -- "$NAME")
SYM=$(readlink "$NAME")
NAME=$(cd "$DIR" > /dev/null && cd "$(dirname -- "$SYM")" > /dev/null && pwd)/$(basename -- "$SYM")
done
echo "$NAME"
}
# Find a CLI version of PHP
findCliPhp() {
for TESTEXEC in php php-cli /usr/local/bin/php
do
SAPI=$(echo "<?= PHP_SAPI ?>" | $TESTEXEC 2>/dev/null)
if [ "$SAPI" = "cli" ]
then
echo $TESTEXEC
return
fi
done
echo "Failed to find a CLI version of PHP; falling back to system standard php executable" >&2
echo "php";
}
# If current path is a symlink, resolve to real path
realname="$0"
if [ -L "$realname" ]
then
realname=$(readlink -f "$0")
fi
CONSOLE=$(dirname -- "$(canonicalize "$realname")")
APP=$(dirname "$CONSOLE")
# If your CLI PHP is somewhere that this doesn't find, you can define a PHP environment
# variable with the correct path in it.
if [ -z "$PHP" ]
then
PHP=$(findCliPhp)
fi
if [ "$(basename "$realname")" != 'cake' ]
then
exec "$PHP" "$CONSOLE"/cake.php "$(basename "$realname")" "$@"
else
exec "$PHP" "$CONSOLE"/cake.php "$@"
fi
exit

27
idrocap_wa/bin/cake.bat

@ -0,0 +1,27 @@
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: Cake is a Windows batch script for invoking CakePHP shell commands
::
:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
::
:: Licensed under The MIT License
:: Redistributions of files must retain the above copyright notice.
::
:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
:: @link https://cakephp.org CakePHP(tm) Project
:: @since 2.0.0
:: @license https://opensource.org/licenses/mit-license.php MIT License
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@echo off
SET app=%0
SET lib=%~dp0
php "%lib%cake.php" %*
echo.
exit /B %ERRORLEVEL%

10
idrocap_wa/bin/cake.php

@ -0,0 +1,10 @@
#!/usr/bin/php -q
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use App\Application;
use Cake\Console\CommandRunner;
// Build the runner with an application and root executable name.
$runner = new CommandRunner(new Application(dirname(__DIR__) . '/config'), 'cake');
exit($runner->run($argv));

67
idrocap_wa/composer.json

@ -0,0 +1,67 @@
{
"name": "cakephp/app",
"description": "CakePHP skeleton app",
"homepage": "https://cakephp.org",
"type": "project",
"license": "MIT",
"require": {
"php": ">=8.1",
"arodu/cakelte": "^2.0",
"aws/aws-sdk-php": "^3.301",
"cakephp/authentication": "^3.0",
"cakephp/cakephp": "^5.0.1",
"cakephp/migrations": "^4.0.0",
"cakephp/plugin-installer": "^2.0",
"firebase/php-jwt": "^6.10",
"friendsofcake/cakephp-csvview": "^5.0",
"lordsimal/cakephp-sentry": "^3.0",
"mobiledetect/mobiledetectlib": "^3.74",
"muffin/trash": "^4.0",
"php-amqplib/php-amqplib": "^3.6",
"simpleenergy/php-webhdfs": "^1.0"
},
"require-dev": {
"cakephp/bake": "^3.0.0",
"cakephp/cakephp-codesniffer": "^5.0",
"cakephp/debug_kit": "^5.0.0",
"josegonzalez/dotenv": "^4.0",
"phpunit/phpunit": "^10.1.0"
},
"suggest": {
"markstory/asset_compress": "An asset compression plugin which provides file concatenation and a flexible filter system for preprocessing and minification.",
"dereuromark/cakephp-ide-helper": "After baking your code, this keeps your annotations in sync with the code evolving from there on for maximum IDE and PHPStan/Psalm compatibility.",
"phpstan/phpstan": "PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code.",
"cakephp/repl": "Console tools for a REPL interface for CakePHP applications."
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Test\\": "tests/",
"Cake\\Test\\": "vendor/cakephp/cakephp/tests/"
}
},
"scripts": {
"post-install-cmd": "App\\Console\\Installer::postInstall",
"post-create-project-cmd": "App\\Console\\Installer::postInstall",
"check": [
"@test",
"@cs-check"
],
"cs-check": "phpcs --colors -p",
"cs-fix": "phpcbf --colors -p",
"stan": "phpstan analyse",
"test": "phpunit --colors=always"
},
"config": {
"platform-check": true,
"sort-packages": true,
"allow-plugins": {
"cakephp/plugin-installer": true,
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}

7466
idrocap_wa/composer.lock generated

File diff soppresso perché troppo grande Load Diff

38
idrocap_wa/config/.env.example

@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Used as a default to seed config/.env which
# enables you to use environment variables to configure
# the aspects of your application that vary by
# environment.
#
# Having this file in production is considered a **SECURITY RISK** and also decreases
# the bootstrap performance of your application.
#
# To use this file, first copy it into `config/.env`. Also ensure the related
# code block for loading this file is uncommented in `config/bootstrap.php`
#
# In development .env files are parsed by PHP
# and set into the environment. This provides a simpler
# development workflow over standard environment variables.
export APP_NAME="__APP_NAME__"
export DEBUG="true"
export APP_ENCODING="UTF-8"
export APP_DEFAULT_LOCALE="en_US"
export APP_DEFAULT_TIMEZONE="UTC"
export SECURITY_SALT="__SALT__"
# Uncomment these to define cache configuration via environment variables.
#export CACHE_DURATION="+2 minutes"
#export CACHE_DEFAULT_URL="file:///path/to/tmp/cache/?prefix=${APP_NAME}_default_&duration=${CACHE_DURATION}"
#export CACHE_CAKECORE_URL="file:///path/to/tmp/cache/persistent?prefix=${APP_NAME}_cake_core_&serialize=true&duration=${CACHE_DURATION}"
#export CACHE_CAKEMODEL_URL="file:///path/to/tmp/cache/models?prefix=${APP_NAME}_cake_model_&serialize=true&duration=${CACHE_DURATION}"
# Uncomment these to define email transport configuration via environment variables.
#export EMAIL_TRANSPORT_DEFAULT_URL=""
# Uncomment these to define database configuration via environment variables.
#export DATABASE_URL="mysql://my_app:secret@localhost/${APP_NAME}?encoding=utf8&timezone=UTC&cacheMetadata=true&quoteIdentifiers=false&persistent=false"
#export DATABASE_TEST_URL="mysql://my_app:secret@localhost/test_${APP_NAME}?encoding=utf8&timezone=UTC&cacheMetadata=true&quoteIdentifiers=false&persistent=false"
# Uncomment these to define logging configuration via environment variables.
#export LOG_DEBUG_URL="file:///path/to/logs/?levels[]=notice&levels[]=info&levels[]=debug&file=debug"
#export LOG_ERROR_URL="file:///path/to/logs/?levels[]=warning&levels[]=error&levels[]=critical&levels[]=alert&levels[]=emergency&file=error"

436
idrocap_wa/config/app.php

@ -0,0 +1,436 @@
<?php
use Cake\Cache\Engine\FileEngine;
use Cake\Database\Connection;
use Cake\Database\Driver\Mysql;
use Cake\Log\Engine\FileLog;
use Cake\Mailer\Transport\MailTransport;
use Cake\Cache\Engine\RedisEngine;
return [
/*
* Debug Level:
*
* Production Mode:
* false: No error messages, errors, or warnings shown.
*
* Development Mode:
* true: Errors and warnings shown.
*/
'debug' => filter_var(env('DEBUG', false), FILTER_VALIDATE_BOOLEAN),
/*
* Configure basic information about the application.
*
* - namespace - The namespace to find app classes under.
* - defaultLocale - The default locale for translation, formatting currencies and numbers, date and time.
* - encoding - The encoding used for HTML + database connections.
* - base - The base directory the app resides in. If false this
* will be auto detected.
* - dir - Name of app directory.
* - webroot - The webroot directory.
* - wwwRoot - The file path to webroot.
* - baseUrl - To configure CakePHP to *not* use mod_rewrite and to
* use CakePHP pretty URLs, remove these .htaccess
* files:
* /.htaccess
* /webroot/.htaccess
* And uncomment the baseUrl key below.
* - fullBaseUrl - A base URL to use for absolute links. When set to false (default)
* CakePHP generates required value based on `HTTP_HOST` environment variable.
* However, you can define it manually to optimize performance or if you
* are concerned about people manipulating the `Host` header.
* - imageBaseUrl - Web path to the public images directory under webroot.
* - cssBaseUrl - Web path to the public css directory under webroot.
* - jsBaseUrl - Web path to the public js directory under webroot.
* - paths - Configure paths for non class based resources. Supports the
* `plugins`, `templates`, `locales` subkeys, which allow the definition of
* paths for plugins, view templates and locale files respectively.
*/
'App' => [
'namespace' => 'App',
'encoding' => env('APP_ENCODING', 'UTF-8'),
'defaultLocale' => env('APP_DEFAULT_LOCALE', 'it'),
'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'UTC'),
'defaultUserTimezone' => 'Europe/Rome',
'base' => false,
'dir' => 'src',
'webroot' => 'webroot',
'wwwRoot' => WWW_ROOT,
//'baseUrl' => env('SCRIPT_NAME'),
'fullBaseUrl' => false,
'imageBaseUrl' => 'img/',
'cssBaseUrl' => 'css/',
'jsBaseUrl' => 'js/',
'paths' => [
'plugins' => [ROOT . DS . 'plugins' . DS],
'templates' => [ROOT . DS . 'templates' . DS],
'locales' => [RESOURCES . 'locales' . DS],
],
],
/*
* Security and encryption configuration
*
* - salt - A random string used in security hashing methods.
* The salt value is also used as the encryption key.
* You should treat it as extremely sensitive data.
*/
'Security' => [
'salt' => env('SECURITY_SALT'),
],
/*
* Apply timestamps with the last modified time to static assets (js, css, images).
* Will append a querystring parameter containing the time the file was modified.
* This is useful for busting browser caches.
*
* Set to true to apply timestamps when debug is true. Set to 'force' to always
* enable timestamping regardless of debug value.
*/
'Asset' => [
//'timestamp' => true,
// 'cacheTime' => '+1 year'
],
/*
* Configure the cache adapters.
*/
'Cache' => [
'default' => [
'className' => RedisEngine::class,
'database' => 1,
'duration' => 3600,
'groups' => [],
'password' => false,
'persistent' => true,
'port' => 6379,
'prefix' => '',
'probability' => 100,
'host' => null,
'server' => 'redis',
'timeout' => 0,
'unix_socket' => false,
],
/*
* Configure the cache used for general framework caching.
* Translation cache files are stored with this configuration.
* Duration will be set to '+2 minutes' in bootstrap.php when debug = true
* If you set 'className' => 'Null' core cache will be disabled.
*/
'_cake_translations_' => [
'className' => FileEngine::class,
'prefix' => 'myapp_cake_core_',
'path' => CACHE . 'persistent' . DS,
'serialize' => true,
'duration' => '+1 years',
'url' => env('CACHE_CAKECORE_URL', null),
],
/*
* Configure the cache for model and datasource caches. This cache
* configuration is used to store schema descriptions, and table listings
* in connections.
* Duration will be set to '+2 minutes' in bootstrap.php when debug = true
*/
'_cake_model_' => [
'className' => FileEngine::class,
'prefix' => 'myapp_cake_model_',
'path' => CACHE . 'models' . DS,
'serialize' => true,
'duration' => '+1 years',
'url' => env('CACHE_CAKEMODEL_URL', null),
],
],
/*
* Configure the Error and Exception handlers used by your application.
*
* By default errors are displayed using Debugger, when debug is true and logged
* by Cake\Log\Log when debug is false.
*
* In CLI environments exceptions will be printed to stderr with a backtrace.
* In web environments an HTML page will be displayed for the exception.
* With debug true, framework errors like Missing Controller will be displayed.
* When debug is false, framework errors will be coerced into generic HTTP errors.
*
* Options:
*
* - `errorLevel` - int - The level of errors you are interested in capturing.
* - `trace` - boolean - Whether backtraces should be included in
* logged errors/exceptions.
* - `log` - boolean - Whether you want exceptions logged.
* - `exceptionRenderer` - string - The class responsible for rendering uncaught exceptions.
* The chosen class will be used for both CLI and web environments. If you want different
* classes used in CLI and web environments you'll need to write that conditional logic as well.
* The conventional location for custom renderers is in `src/Error`. Your exception renderer needs to
* implement the `render()` method and return either a string or Http\Response.
* `errorRenderer` - string - The class responsible for rendering PHP errors. The selected
* class will be used for both web and CLI contexts. If you want different classes for each environment
* you'll need to write that conditional logic as well. Error renderers need to
* to implement the `Cake\Error\ErrorRendererInterface`.
* - `skipLog` - array - List of exceptions to skip for logging. Exceptions that
* extend one of the listed exceptions will also be skipped for logging.
* E.g.:
* `'skipLog' => ['Cake\Http\Exception\NotFoundException', 'Cake\Http\Exception\UnauthorizedException']`
* - `extraFatalErrorMemory` - int - The number of megabytes to increase the memory limit by
* when a fatal error is encountered. This allows
* breathing room to complete logging or error handling.
* - `ignoredDeprecationPaths` - array - A list of glob compatible file paths that deprecations
* should be ignored in. Use this to ignore deprecations for plugins or parts of
* your application that still emit deprecations.
*/
'Error' => [
'errorLevel' => E_ALL,
'skipLog' => [],
'log' => true,
'trace' => true,
'ignoredDeprecationPaths' => [],
'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
],
/*
* Debugger configuration
*
* Define development error values for Cake\Error\Debugger
*
* - `editor` Set the editor URL format you want to use.
* By default atom, emacs, macvim, phpstorm, sublime, textmate, and vscode are
* available. You can add additional editor link formats using
* `Debugger::addEditor()` during your application bootstrap.
* - `outputMask` A mapping of `key` to `replacement` values that
* `Debugger` should replace in dumped data and logs generated by `Debugger`.
*/
'Debugger' => [
'editor' => 'phpstorm',
],
/*
* Email configuration.
*
* By defining transports separately from delivery profiles you can easily
* re-use transport configuration across multiple profiles.
*
* You can specify multiple configurations for production, development and
* testing.
*
* Each transport needs a `className`. Valid options are as follows:
*
* Mail - Send using PHP mail function
* Smtp - Send using SMTP
* Debug - Do not send the email, just return the result
*
* You can add custom transports (or override existing transports) by adding the
* appropriate file to src/Mailer/Transport. Transports should be named
* 'YourTransport.php', where 'Your' is the name of the transport.
*/
'EmailTransport' => [
'default' => [
'className' => MailTransport::class,
/*
* The keys host, port, timeout, username, password, client and tls
* are used in SMTP transports
*/
'host' => 'localhost',
'port' => 25,
'timeout' => 30,
/*
* It is recommended to set these options through your environment or app_local.php
*/
//'username' => null,
//'password' => null,
'client' => null,
'tls' => false,
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
],
],
/*
* Email delivery profiles
*
* Delivery profiles allow you to predefine various properties about email
* messages from your application and give the settings a name. This saves
* duplication across your application and makes maintenance and development
* easier. Each profile accepts a number of keys. See `Cake\Mailer\Email`
* for more information.
*/
'Email' => [
'default' => [
'transport' => 'default',
'from' => 'you@localhost',
/*
* Will by default be set to config value of App.encoding, if that exists otherwise to UTF-8.
*/
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
],
],
/*
* Connection information used by the ORM to connect
* to your application's datastores.
*
* ### Notes
* - Drivers include Mysql Postgres Sqlite Sqlserver
* See vendor\cakephp\cakephp\src\Database\Driver for complete list
* - Do not use periods in database name - it may lead to error.
* See https://github.com/cakephp/cakephp/issues/6471 for details.
* - 'encoding' is recommended to be set to full UTF-8 4-Byte support.
* E.g set it to 'utf8mb4' in MariaDB and MySQL and 'utf8' for any
* other RDBMS.
*/
'Datasources' => [
/*
* These configurations should contain permanent settings used
* by all environments.
*
* The values in app_local.php will override any values set here
* and should be used for local and per-environment configurations.
*
* Environment variable based configurations can be loaded here or
* in app_local.php depending on the applications needs.
*/
'default' => [
'className' => Connection::class,
'driver' => Mysql::class,
'persistent' => false,
'timezone' => 'UTC',
/*
* For MariaDB/MySQL the internal default changed from utf8 to utf8mb4, aka full utf-8 support, in CakePHP 3.6
*/
//'encoding' => 'utf8mb4',
/*
* If your MySQL server is configured with `skip-character-set-client-handshake`
* then you MUST use the `flags` config to set your charset encoding.
* For e.g. `'flags' => [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4']`
*/
'flags' => [],
'cacheMetadata' => true,
'log' => false,
/*
* Set identifier quoting to true if you are using reserved words or
* special characters in your table or column names. Enabling this
* setting will result in queries built using the Query Builder having
* identifiers quoted when creating SQL. It should be noted that this
* decreases performance because each query needs to be traversed and
* manipulated before being executed.
*/
'quoteIdentifiers' => true,
/*
* During development, if using MySQL < 5.6, uncommenting the
* following line could boost the speed at which schema metadata is
* fetched from the database. It can also be set directly with the
* mysql configuration directive 'innodb_stats_on_metadata = 0'
* which is the recommended value in production environments
*/
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
],
'geo' => [
'className' => Connection::class,
'driver' => Mysql::class,
'persistent' => false,
'timezone' => 'UTC',
'encoding' => 'utf8',
'flags' => [],
'cacheMetadata' => true,
'log' => false,
'quoteIdentifiers' => false,
],
/*
* The test connection is used during the test suite.
*/
'test' => [
'className' => Connection::class,
'driver' => Mysql::class,
'persistent' => false,
'timezone' => 'UTC',
//'encoding' => 'utf8mb4',
'flags' => [],
'cacheMetadata' => true,
'quoteIdentifiers' => false,
'log' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
],
],
/*
* Configures logging options
*/
'Log' => [
'debug' => [
'className' => FileLog::class,
'path' => LOGS,
'file' => 'debug',
'url' => env('LOG_DEBUG_URL', null),
'scopes' => null,
'levels' => ['notice', 'info', 'debug'],
],
'error' => [
'className' => FileLog::class,
'path' => LOGS,
'file' => 'error',
'url' => env('LOG_ERROR_URL', null),
'scopes' => null,
'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
],
// To enable this dedicated query log, you need set your datasource's log flag to true
'queries' => [
'className' => FileLog::class,
'path' => LOGS,
'file' => 'queries',
'url' => env('LOG_QUERIES_URL', null),
'scopes' => ['cake.database.queries'],
],
],
/*
* Session configuration.
*
* Contains an array of settings to use for session configuration. The
* `defaults` key is used to define a default preset to use for sessions, any
* settings declared here will override the settings of the default config.
*
* ## Options
*
* - `cookie` - The name of the cookie to use. Defaults to value set for `session.name` php.ini config.
* Avoid using `.` in cookie names, as PHP will drop sessions from cookies with `.` in the name.
* - `cookiePath` - The url path for which session cookie is set. Maps to the
* `session.cookie_path` php.ini config. Defaults to base path of app.
* - `timeout` - The time in minutes the session should be valid for.
* Pass 0 to disable checking timeout.
* Please note that php.ini's session.gc_maxlifetime must be equal to or greater
* than the largest Session['timeout'] in all served websites for it to have the
* desired effect.
* - `defaults` - The default configuration set to use as a basis for your session.
* There are four built-in options: php, cake, cache, database.
* - `handler` - Can be used to enable a custom session handler. Expects an
* array with at least the `engine` key, being the name of the Session engine
* class to use for managing the session. CakePHP bundles the `CacheSession`
* and `DatabaseSession` engines.
* - `ini` - An associative array of additional 'session.*` ini values to set.
*
* The built-in `defaults` options are:
*
* - 'php' - Uses settings defined in your php.ini.
* - 'cake' - Saves session files in CakePHP's /tmp directory.
* - 'database' - Uses CakePHP's database sessions.
* - 'cache' - Use the Cache class to save sessions.
*
* To define a custom session handler, save it at src/Http/Session/<name>.php.
* Make sure the class implements PHP's `SessionHandlerInterface` and set
* Session.handler to <name>
*
* To use database sessions, load the SQL file located at config/schema/sessions.sql
*/
'Session' => [
'cookie' => 'JIXEL',
'defaults' => 'cache',
],
];

94
idrocap_wa/config/app_local.example.php

@ -0,0 +1,94 @@
<?php
/*
* Local configuration file to provide any overrides to your app.php configuration.
* Copy and save this file as app_local.php and make changes as required.
* Note: It is not recommended to commit files with credentials such as app_local.php
* into source code version control.
*/
return [
/*
* Debug Level:
*
* Production Mode:
* false: No error messages, errors, or warnings shown.
*
* Development Mode:
* true: Errors and warnings shown.
*/
'debug' => filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN),
/*
* Security and encryption configuration
*
* - salt - A random string used in security hashing methods.
* The salt value is also used as the encryption key.
* You should treat it as extremely sensitive data.
*/
'Security' => [
'salt' => env('SECURITY_SALT', '__SALT__'),
],
/*
* Connection information used by the ORM to connect
* to your application's datastores.
*
* See app.php for more configuration options.
*/
'Datasources' => [
'default' => [
'host' => 'localhost',
/*
* CakePHP will use the default DB port based on the driver selected
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
* the following line and set the port accordingly
*/
//'port' => 'non_standard_port_number',
'username' => 'my_app',
'password' => 'secret',
'database' => 'my_app',
/*
* If not using the default 'public' schema with the PostgreSQL driver
* set it here.
*/
//'schema' => 'myapp',
/*
* You can use a DSN string to set the entire configuration
*/
'url' => env('DATABASE_URL', null),
],
/*
* The test connection is used during the test suite.
*/
'test' => [
'host' => 'localhost',
//'port' => 'non_standard_port_number',
'username' => 'my_app',
'password' => 'secret',
'database' => 'test_myapp',
//'schema' => 'myapp',
'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tmp/tests.sqlite'),
],
],
/*
* Email configuration.
*
* Host and credential configuration in case you are using SmtpTransport
*
* See app.php for more configuration options.
*/
'EmailTransport' => [
'default' => [
'host' => 'localhost',
'port' => 25,
'username' => null,
'password' => null,
'client' => null,
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
],
],
];

94
idrocap_wa/config/app_local.php

@ -0,0 +1,94 @@
<?php
/*
* Local configuration file to provide any overrides to your app.php configuration.
* Copy and save this file as app_local.php and make changes as required.
* Note: It is not recommended to commit files with credentials such as app_local.php
* into source code version control.
*/
return [
/*
* Debug Level:
*
* Production Mode:
* false: No error messages, errors, or warnings shown.
*
* Development Mode:
* true: Errors and warnings shown.
*/
'debug' => filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN),
/*
* Security and encryption configuration
*
* - salt - A random string used in security hashing methods.
* The salt value is also used as the encryption key.
* You should treat it as extremely sensitive data.
*/
'Security' => [
'salt' => env('SECURITY_SALT', '1d8f10d21d25f4efea479140b503d0c32a7aac909d93d0a48bb9e153d49a0449'),
],
/*
* Connection information used by the ORM to connect
* to your application's datastores.
*
* See app.php for more configuration options.
*/
'Datasources' => [
'default' => [
'host' => 'localhost',
/*
* CakePHP will use the default DB port based on the driver selected
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
* the following line and set the port accordingly
*/
//'port' => 'non_standard_port_number',
'username' => 'my_app',
'password' => 'secret',
'database' => 'my_app',
/*
* If not using the default 'public' schema with the PostgreSQL driver
* set it here.
*/
//'schema' => 'myapp',
/*
* You can use a DSN string to set the entire configuration
*/
'url' => env('DATABASE_URL', null),
],
/*
* The test connection is used during the test suite.
*/
'test' => [
'host' => 'localhost',
//'port' => 'non_standard_port_number',
'username' => 'my_app',
'password' => 'secret',
'database' => 'test_myapp',
//'schema' => 'myapp',
'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tmp/tests.sqlite'),
],
],
/*
* Email configuration.
*
* Host and credential configuration in case you are using SmtpTransport
*
* See app.php for more configuration options.
*/
'EmailTransport' => [
'default' => [
'host' => 'localhost',
'port' => 25,
'username' => null,
'password' => null,
'client' => null,
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
],
],
];

231
idrocap_wa/config/bootstrap.php

@ -0,0 +1,231 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.8
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
/*
* Configure paths required to find CakePHP + general filepath constants
*/
require __DIR__ . DIRECTORY_SEPARATOR . 'paths.php';
/*
* Bootstrap CakePHP.
*
* Does the various bits of setup that CakePHP needs to do.
* This includes:
*
* - Registering the CakePHP autoloader.
* - Setting the default application paths.
*/
require CORE_PATH . 'config' . DS . 'bootstrap.php';
use Cake\Cache\Cache;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Database\Type\StringType;
use Cake\Database\TypeFactory;
use Cake\Datasource\ConnectionManager;
use Cake\Error\ErrorTrap;
use Cake\Error\ExceptionTrap;
use Cake\Http\ServerRequest;
use Cake\Log\Log;
use Cake\Mailer\Mailer;
use Cake\Mailer\TransportFactory;
use Cake\Routing\Router;
use Cake\Utility\Security;
/**
* Load global functions.
*/
require CAKE . 'functions.php';
/*
* See https://github.com/josegonzalez/php-dotenv for API details.
*
* Uncomment block of code below if you want to use `.env` file during development.
* You should copy `config/.env.example` to `config/.env` and set/modify the
* variables as required.
*
* The purpose of the .env file is to emulate the presence of the environment
* variables like they would be present in production.
*
* If you use .env files, be careful to not commit them to source control to avoid
* security risks. See https://github.com/josegonzalez/php-dotenv#general-security-information
* for more information for recommended practices.
*/
// if (!env('APP_NAME') && file_exists(CONFIG . '.env')) {
// $dotenv = new \josegonzalez\Dotenv\Loader([CONFIG . '.env']);
// $dotenv->parse()
// ->putenv()
// ->toEnv()
// ->toServer();
// }
/*
* Read configuration file and inject configuration into various
* CakePHP classes.
*
* By default there is only one configuration file. It is often a good
* idea to create multiple configuration files, and separate the configuration
* that changes from configuration that does not. This makes deployment simpler.
*/
try {
Configure::config('default', new PhpConfig());
Configure::load('app', 'default', false);
} catch (\Exception $e) {
exit($e->getMessage() . "\n");
}
/*
* Load an environment local configuration file to provide overrides to your configuration.
* Notice: For security reasons app_local.php **should not** be included in your git repo.
*/
if (file_exists(CONFIG . 'app_local.php')) {
Configure::load('app_local', 'default');
}
/*
* When debug = true the metadata cache should only last
* for a short time.
*/
if (Configure::read('debug')) {
Configure::write('Cache._cake_model_.duration', '+2 minutes');
Configure::write('Cache._cake_core_.duration', '+2 minutes');
// disable router cache during development
Configure::write('Cache._cake_routes_.duration', '+2 seconds');
}
/*
* Set the default server timezone. Using UTC makes time calculations / conversions easier.
* Check https://php.net/manual/en/timezones.php for list of valid timezone strings.
*/
date_default_timezone_set(Configure::read('App.defaultTimezone'));
/*
* Configure the mbstring extension to use the correct encoding.
*/
mb_internal_encoding(Configure::read('App.encoding'));
/*
* Set the default locale. This controls how dates, number and currency is
* formatted and sets the default language to use for translations.
*/
ini_set('intl.default_locale', Configure::read('App.defaultLocale'));
/*
* Register application error and exception handlers.
*/
(new ErrorTrap(Configure::read('Error')))->register();
(new ExceptionTrap(Configure::read('Error')))->register();
/*
* Include the CLI bootstrap overrides.
*/
if (PHP_SAPI === 'cli') {
require CONFIG . 'bootstrap_cli.php';
}
/*
* Set the full base URL.
* This URL is used as the base of all absolute links.
*/
$fullBaseUrl = Configure::read('App.fullBaseUrl');
if (!$fullBaseUrl) {
/*
* When using proxies or load balancers, SSL/TLS connections might
* get terminated before reaching the server. If you trust the proxy,
* you can enable `$trustProxy` to rely on the `X-Forwarded-Proto`
* header to determine whether to generate URLs using `https`.
*
* See also https://book.cakephp.org/4/en/controllers/request-response.html#trusting-proxy-headers
*/
$trustProxy = false;
$s = null;
if (env('HTTPS') || ($trustProxy && env('HTTP_X_FORWARDED_PROTO') === 'https')) {
$s = 's';
}
$httpHost = env('HTTP_HOST');
if (isset($httpHost)) {
$fullBaseUrl = 'http' . $s . '://' . $httpHost;
}
unset($httpHost, $s);
}
if ($fullBaseUrl) {
Router::fullBaseUrl($fullBaseUrl);
}
unset($fullBaseUrl);
Cache::setConfig(Configure::consume('Cache'));
ConnectionManager::setConfig(Configure::consume('Datasources'));
TransportFactory::setConfig(Configure::consume('EmailTransport'));
Mailer::setConfig(Configure::consume('Email'));
Log::setConfig(Configure::consume('Log'));
Security::setSalt(Configure::consume('Security.salt'));
/*
* Setup detectors for mobile and tablet.
* If you don't use these checks you can safely remove this code
* and the mobiledetect package from composer.json.
*/
ServerRequest::addDetector('mobile', function ($request) {
$detector = new \Detection\MobileDetect();
return $detector->isMobile();
});
ServerRequest::addDetector('tablet', function ($request) {
$detector = new \Detection\MobileDetect();
return $detector->isTablet();
});
/*
* You can enable default locale format parsing by adding calls
* to `useLocaleParser()`. This enables the automatic conversion of
* locale specific date formats. For details see
* @link https://book.cakephp.org/4/en/core-libraries/internationalization-and-localization.html#parsing-localized-datetime-data
*/
// \Cake\Database\TypeFactory::build('time')
// ->useLocaleParser();
// \Cake\Database\TypeFactory::build('date')
// ->useLocaleParser();
// \Cake\Database\TypeFactory::build('datetime')
// ->useLocaleParser();
// \Cake\Database\TypeFactory::build('timestamp')
// ->useLocaleParser();
// \Cake\Database\TypeFactory::build('datetimefractional')
// ->useLocaleParser();
// \Cake\Database\TypeFactory::build('timestampfractional')
// ->useLocaleParser();
// \Cake\Database\TypeFactory::build('datetimetimezone')
// ->useLocaleParser();
// \Cake\Database\TypeFactory::build('timestamptimezone')
// ->useLocaleParser();
/*
* Custom Inflector rules, can be set to correctly pluralize or singularize
* table, model, controller names or whatever other string is passed to the
* inflection functions.
*/
//Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']);
//Inflector::rules('irregular', ['red' => 'redlings']);
//Inflector::rules('uninflected', ['dontinflectme']);
// set a custom date and time format
// see https://book.cakephp.org/4/en/core-libraries/time.html#setting-the-default-locale-and-format-string
// and https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
//\Cake\I18n\FrozenDate::setToStringFormat('dd.MM.yyyy');
//\Cake\I18n\FrozenTime::setToStringFormat('dd.MM.yyyy HH:mm');

35
idrocap_wa/config/bootstrap_cli.php

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
use Cake\Core\Configure;
/*
* Additional bootstrapping and configuration for CLI environments should
* be put here.
*/
// Set the fullBaseUrl to allow URLs to be generated in shell tasks.
// This is useful when sending email from shells.
//Configure::write('App.fullBaseUrl', php_uname('n'));
// Set logs to different files so they don't have permission conflicts.
if (Configure::check('Log.debug')) {
Configure::write('Log.debug.file', 'cli-debug');
}
if (Configure::check('Log.error')) {
Configure::write('Log.error.file', 'cli-error');
}

43
idrocap_wa/config/cakelte.php

@ -0,0 +1,43 @@
<?php
use CakeLte\Style\Header;
use CakeLte\Style\Sidebar;
use Cake\Core\Configure;
return [
'CakeLte' => [
'app-name' => '<b>' . Configure::read('Theme.title') . '</b>',
'app-logo' => Configure::read('Theme.logo'),
'small-text' => false,
'dark-mode' => false,
'layout-boxed' => false,
'header' => [
'fixed' => false,
'border' => true,
'style' => Header::STYLE_DARK,
'dropdown-legacy' => false,
],
'sidebar' => [
'fixed' => true,
'collapsed' => false,
'mini' => true,
'mini-md' => false,
'mini-xs' => false,
'style' => Sidebar::STYLE_DARK_PRIMARY,
'flat-style' => false,
'legacy-style' => false,
'compact' => false,
'child-indent' => true,
'child-hide-collapse' => false,
'disabled-auto-expand' => false,
],
'footer' => [
'fixed' => false,
],
],
];

52
idrocap_wa/config/keys/private.key

@ -0,0 +1,52 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEA0YVnol1T7RaJIiMDiivC1bgDyqn2b31j+t8NQXcLm7dOq8az
Wgsn3ySGj1Was/Mlye5uCegnj9LPOvK6b6wG4NcNKEouAdjwoZ8PFsTZU2q/lGzr
Ze4Kc/E19oVgdKRhTgDuCKq7sShC7Un+2lY0CCt2GSA133Mqoztih5Y+zvlqyKaM
xF6eNy62L1IqNUWEMFIgkVq4Jy2XMii7qrYdE9cJbleZ1MDlTO7a0v3bhLxdC5D6
2HPcalV3qYKiCWX/EhCz4b8JWfoW8tfV8GoEX+6JV+AohOXC9wdopCV89SJu1x+S
p4yWkBtm1nSMifaPfXqyz9tiGFL8ziJpEW59lTgbd1KZMCcoGIHR8gq7PowDVkAQ
IdU628DS3FPgBgPRV7r9oPuytjPzojmuu64aClgWylE6WDn2daBHUHtlY0NLA7VN
jIMMnvnlM0OBGGN4god4LVzFZkgso36i6RJghYUiKaz7DOQEMEGx6bWQlHxnLR0/
khv/1ERpKNWq/q0vXeoNA7L1jZ64k3U/MyWmaJPdjcoPFGYI2Vwlz//M1TZmyoV7
YC01YJcsTbA5+ocukOZDlvsEWZKWJoi/I5vYcQfBZGrAY8pl7LFCVlnpUR+UHB3a
Fp0+Q4OK0OhQuOmbMqz2J/t2MbWXwBsnQkP3XNnoA1t4fbraPeex25NTZVMCAwEA
AQKCAgEAjnRkzQkleeq7nIEv8+7jDsqJkW7UBmO76aNkcOFO/DW7AKId6ZfyKQ69
ipEKmVU1mUFKgePdmjUb/xlv12OgXZj58i3VcB4fJdRuvu1iUgwfa/xOZMRfSQt0
xENVHJlzK0UIks36xW35azrHU44O3IsoRdcXqfC0yb0jtcpatADzMK/Ev/MRIbXZ
2vzDg+rbALumNw32bXTa+xik2GHwDUBEwkg/aF8qyI8SKyi861fi721IkIjeEVJB
BPiz0S4PKio/E7r+0bVvivdjF44NoWge66eWBR3u3GSmKgzvFACwWgW2gcLdz+dB
mYgu0dLZJtiY4ShpXt510nWk+Fwmd2YHSt0U/rnoiOSEXRqh2UPZL9zgK0F81E8v
Ay1Yij59P0tkjIz3VtYluMGV6nXoQunJyIFsUN3r3h2p6ZxeitN+CEmUN/zOvmrN
Ziqy4Hnta03eC3ZZ3i3NIHuth63hVrh5qFEKT5Ggqvw6Swfzg1mL9biZ+4A1xICP
8850+yBaX6n3BgRwLmFd9xjtA7GNJ1vrcOn6+uw7OOaUl+WUlnX8uqDCPE/mhMd4
SM/O4kL/G4BFb45q4LeYkiuFk5UHatWdcOlttQkvrgSCH4PpfJDiilKn/tVU34HY
OM7/N+j9wMpZj7dqPMra+WbzKA4I0d+rnKZsUVHbCAZZzEf0w/kCggEBAPRQKV8W
w1Z+JlZeRtoxSSCSEgHDpun13ZiXBi17ErYhN5LTCMJ8XUmeeexVCcRMOqjvClgz
2hrHaC66nW2pfBu2euVHqecD0likKEINsucbTa2PNZeP0sV8px1k5NASRlNilqGy
vrWT425E7yrss2RBOcE6ro6M9QYZSLZjiYNqzk6w4j6McyFertq7/Yz9JCaAq72e
ptCrvzoAtCFra3nwTgxlu1QVB8I/Kjg94lpTLQcS7mehDep+qYSlnFzJ5CnGF7t4
K5WZ4VEo1u4dpbewTVDfc3W+ODTQgmYByU6mUwl4Kf4V6ovKJ9say1z0ZdbXcHRJ
ZG8+Hdld47TwHe8CggEBANuLLtsoCvUyLs9X6+6kToSTN3FVRnkfoJLbXe9+smw/
5QAvvh2lwlwc6xXajEEsNK8z9mVRSCrXoiqRTjTo2ioe0oxx9Gnou+/d2MB9WJm3
hQAwMGjwifg6AD9bZre+NiEl92WmlJs3fm4zFrEMpCBCOJ7JIe7GxsdKwR/guvQt
9k/NCQj2fOnCzCoFYtebPPDXukcn2Ad2XOkvLWzLmG4HRGuLPeqgjvxppBdtU3Xm
NJklIndw1g+xcY8/7bbNyo8Nd/PV/ymv/sflrQyXjVs4mwwAgY9usFntO+qT4nEd
wfPgedDlpsNDprSdjDo5xNA79yaVFUuzJCIDFXO9Ut0CggEAaA5QYKCoWAlouLZ0
xO/i7o1oP/od+F3woNIKHorhwFLLgayCeZtQfE+ULFVX0JSLE3KkXEbRaUBvHh/7
dz6NjpwdIdRxB+S3zWs1flSpUwdtATQSplfqdJSP9NQJJJnz/AXslDg2Q84WOtv/
Za/lBBlHyQX+XNaqcYuJTa19X0y5TAjIg818J8VVAfK5njgfEosRGrUc5ZmQ29p6
3hULdKcmBt+5dqlyYYhXqhapdsEgV3f3tImDDNIfz1BeFaMjm6s0ZnNbVN9yeQfE
6ywTbfYYKA7T9V0sQUphj6LJI8dxbIISmJarDBjw+sAhx5iMW4Hs8opjzYGOYws7
NdBjXwKCAQBc0Bh8+g633gFAEtFhUAEJtxsHRJ/IcQB3VVn+97cCYHi1fXmIGx1x
x5/y2zPUEZYsQ3F1H8m8ovChwlAnuhERIXqteTd01rktDMKnjdY5FgaaL8UneBk7
4XldT4y7PSSxGtXeGXBVtj4y5FJBgzCXmqdJKYq44TwD4KyIh39B33R0BCA/s8TX
Jk0drq08rRo0T18m2cdQUE+zHQi9J81HJrdcJ7+DQ2FvgFGovcU84Jd37uUFTaSR
ediiUMp0L8fkdjMx2FRJEWhKpeRkoGVYHTQe/M/JDWhsfAiKgP6IB6aJLWsSTyQT
BLSjQr+XlfiId/FiVEBugktSPa+CAJXZAoIBAQDj32TA+MI5gWtWlP4u9KsyrDGD
NJyutrAGziQSibgj5CW++fVm6TcXZ3rezxFZ/QDr8MlEJhpGoi3Ca+U0JyIcrn3z
fgwKGJd7kVFD8jhqm85j8rMk9sqEgg88cE6yt4tC+LAwLLeyhVqea5q5vF5dV9gN
1LtP6E30MQxWRBWhcp7FyZEzS6eYIjQwVCaVD2tDXg3hQQ/gxyPantsEdgoqCfgu
MjBJiaw5fO+qBROzema0LDXs7m5HAZRur22c9a45d0+SmCAmudsxoKrnV6w7gdd+
1Jp1KFef7gx9iSBm3E3rcCBBOfPfRKbvBD5bhNXxT/a/iK8wPQY4uKiwgKt+
-----END RSA PRIVATE KEY-----

15
idrocap_wa/config/keys/public.key

@ -0,0 +1,15 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0YVnol1T7RaJIiMDiivC
1bgDyqn2b31j+t8NQXcLm7dOq8azWgsn3ySGj1Was/Mlye5uCegnj9LPOvK6b6wG
4NcNKEouAdjwoZ8PFsTZU2q/lGzrZe4Kc/E19oVgdKRhTgDuCKq7sShC7Un+2lY0
CCt2GSA133Mqoztih5Y+zvlqyKaMxF6eNy62L1IqNUWEMFIgkVq4Jy2XMii7qrYd
E9cJbleZ1MDlTO7a0v3bhLxdC5D62HPcalV3qYKiCWX/EhCz4b8JWfoW8tfV8GoE
X+6JV+AohOXC9wdopCV89SJu1x+Sp4yWkBtm1nSMifaPfXqyz9tiGFL8ziJpEW59
lTgbd1KZMCcoGIHR8gq7PowDVkAQIdU628DS3FPgBgPRV7r9oPuytjPzojmuu64a
ClgWylE6WDn2daBHUHtlY0NLA7VNjIMMnvnlM0OBGGN4god4LVzFZkgso36i6RJg
hYUiKaz7DOQEMEGx6bWQlHxnLR0/khv/1ERpKNWq/q0vXeoNA7L1jZ64k3U/MyWm
aJPdjcoPFGYI2Vwlz//M1TZmyoV7YC01YJcsTbA5+ocukOZDlvsEWZKWJoi/I5vY
cQfBZGrAY8pl7LFCVlnpUR+UHB3aFp0+Q4OK0OhQuOmbMqz2J/t2MbWXwBsnQkP3
XNnoA1t4fbraPeex25NTZVMCAwEAAQ==
-----END PUBLIC KEY-----

94
idrocap_wa/config/paths.php

@ -0,0 +1,94 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license MIT License (https://opensource.org/licenses/mit-license.php)
*/
/*
* Use the DS to separate the directories in other defines
*/
if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
/*
* These defines should only be edited if you have cake installed in
* a directory layout other than the way it is distributed.
* When using custom settings be sure to use the DS and do not add a trailing DS.
*/
/*
* The full path to the directory which holds "src", WITHOUT a trailing DS.
*/
define('ROOT', dirname(__DIR__));
/*
* The actual directory name for the application directory. Normally
* named 'src'.
*/
define('APP_DIR', 'src');
/*
* Path to the application's directory.
*/
define('APP', ROOT . DS . APP_DIR . DS);
/*
* Path to the config directory.
*/
define('CONFIG', ROOT . DS . 'config' . DS);
/*
* File path to the webroot directory.
*
* To derive your webroot from your webserver change this to:
*
* `define('WWW_ROOT', rtrim($_SERVER['DOCUMENT_ROOT'], DS) . DS);`
*/
define('WWW_ROOT', ROOT . DS . 'webroot' . DS);
/*
* Path to the tests directory.
*/
define('TESTS', ROOT . DS . 'tests' . DS);
/*
* Path to the temporary files directory.
*/
define('TMP', ROOT . DS . 'tmp' . DS);
/*
* Path to the logs directory.
*/
define('LOGS', ROOT . DS . 'logs' . DS);
/*
* Path to the cache files directory. It can be shared between hosts in a multi-server setup.
*/
define('CACHE', TMP . 'cache' . DS);
/*
* Path to the resources directory.
*/
define('RESOURCES', ROOT . DS . 'resources' . DS);
/*
* The absolute path to the "cake" directory, WITHOUT a trailing DS.
*
* CakePHP should always be installed with composer, so look there.
*/
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp');
/*
* Path to the cake directory.
*/
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
define('CAKE', CORE_PATH . 'src' . DS);

15
idrocap_wa/config/plugins.php

@ -0,0 +1,15 @@
<?php
return [
'DebugKit' => [
'onlyDebug' => true,
],
'Bake' => [
'onlyCli' => true,
'optional' => true,
],
'Migrations' => [
'onlyCli' => true,
],
'CakeLte' => [],
];

357
idrocap_wa/config/routes.php

@ -0,0 +1,357 @@
<?php
/**
* Routes configuration.
*
* In this file, you set up routes to your controllers and their actions.
* Routes are very important mechanism that allows you to freely connect
* different URLs to chosen controllers and their actions (functions).
*
* It's loaded within the context of `Application::routes()` method which
* receives a `RouteBuilder` instance `$routes` as method argument.
*
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
use Cake\Routing\RouteBuilder;
use Cake\Routing\Route\InflectedRoute;
return static function (RouteBuilder $routes) {
$routes->setRouteClass(InflectedRoute::class);
$routes->prefix('Api', function (RouteBuilder $routes) {
$routes->setExtensions(['json', 'csv']);
$routes->resources('Attachments', [
'id' => '.*',
]);
$routes->resources('Notifications', [
'map' => [
'' => [
'action' => 'index',
'method' => ['GET'],
],
'index' => [
'action' => 'index',
'method' => ['GET'],
],
'count' => [
'action' => 'count',
'method' => ['GET'],
],
'edit/{id}' => [
'action' => 'edit',
'method' => ['POST'],
],
'check' => [
'action' => 'check',
'method' => ['GET'],
],
],
]);
$routes->resources('Organisations', [
'connectOptions' => [
'photo' => '.*',
'pass' => ['id', 'photo'],
],
'map' => [
'get_photo/{photo}' => [
'action' => 'getPhoto',
'method' => ['GET'],
],
],
]);
$routes->resources('OrganisationTypes', ['inflect' => 'underscore']);
$routes->resources('Users', [
'connectOptions' => [
'photo' => '.*',
'pass' => ['id', 'photo'],
],
'map' => [
'login' => [
'action' => 'login',
'method' => ['POST', 'GET'],
],
'get_photo/{photo}' => [
'action' => 'getPhoto',
'method' => ['GET'],
],
],
]);
$routes->resources('Subscriptions', [
'map' => [
'subscribe' => [
'action' => 'subscribe',
'method' => ['POST'],
],
'unsubscribe' => [
'action' => 'unsubscribe',
'method' => ['DELETE'],
],
],
]);
$routes->resources('WaterDrawingPaperworks', [
'inflect' => 'underscore',
'map' => [
'' => [
'action' => 'index',
'method' => ['GET'],
],
'index' => [
'action' => 'index',
'method' => ['GET'],
],
'view/{id}' => [
'action' => 'view',
'method' => ['GET'],
],
],
]);
$routes->resources('WaterDrawingDerivations', [
'inflect' => 'underscore',
'map' => [
'update_data' => [
'action' => 'update_data',
'method' => ['POST'],
],
'update_location' => [
'action' => 'update_location',
'method' => ['POST'],
],
'view/{id}' => [
'action' => 'view',
'method' => ['GET'],
],
],
]);
$routes->resources('WaterDrawingMeasurements', [
'inflect' => 'underscore',
'map' => [
'add' => [
'action' => 'add',
'method' => ['POST'],
],
'last_index/{id}' => [
'action' => 'last_index',
'method' => ['GET'],
],
],
]);
});
$routes->scope('/', function (RouteBuilder $builder) {
$builder->setExtensions(['json', 'csv']);
$builder->connect('/', ['controller' => 'Dashboard', 'action' => 'index']);
$builder->connect('/attachments/view/{id}', ['controller' => 'Attachments', 'action' => 'view'])->setPass(['id']);
$builder->connect('/attachments/markAsRelevant/{id}', ['controller' => 'Attachments', 'action' => 'markAsRelevant'])->setPass(['id']);
$builder->connect('/attachments/markAsDeleted/{id}', ['controller' => 'Attachments', 'action' => 'markAsDeleted'])->setPass(['id']);
$builder->connect('/capabilities', ['controller' => 'Capabilities', 'action' => 'index']);
$builder->connect('/capabilities/index', ['controller' => 'Capabilities', 'action' => 'index']);
$builder->connect('/capabilities/hide/{id}', ['controller' => 'Capabilities', 'action' => 'hide'])->setPass(['id']);
$builder->connect('/capabilities/show/{id}', ['controller' => 'Capabilities', 'action' => 'show'])->setPass(['id']);
$builder->connect('/capabilities/delete/{id}', ['controller' => 'Capabilities', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/capabilities/restore/{id}', ['controller' => 'Capabilities', 'action' => 'restore'])->setPass(['id']);
$builder->connect('/controllable_objects/getAttachments/{id}', ['controller' => 'ControllableObjects', 'action' => 'getAttachments'])->setPass(['id']);
$builder->connect('/deliveries/{id}', ['controller' => 'Deliveries', 'action' => 'index'])->setPass(['id']);
$builder->connect('/deliveries/index/{id}', ['controller' => 'Deliveries', 'action' => 'index'])->setPass(['id']);
$builder->connect('/deliveries/delete/{id}', ['controller' => 'Deliveries', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/emails/add/{id}', ['controller' => 'Emails', 'action' => 'add'])->setPass(['id']);
$builder->connect('/emails/edit/{id}', ['controller' => 'Emails', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/faxes/add/{id}', ['controller' => 'Faxes', 'action' => 'add'])->setPass(['id']);
$builder->connect('/faxes/edit/{id}', ['controller' => 'Faxes', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/filters/getFilterInput/{id}', ['controller' => 'Filters', 'action' => 'getFilterInput'])->setPass(['id']);
$builder->connect('/groups', ['controller' => 'Groups', 'action' => 'index']);
$builder->connect('/groups/index', ['controller' => 'Groups', 'action' => 'index']);
$builder->connect('/groups/add', ['controller' => 'Groups', 'action' => 'add']);
$builder->connect('/groups/view/{id}', ['controller' => 'Groups', 'action' => 'view'])->setPass(['id']);
$builder->connect('/groups/edit/{id}', ['controller' => 'Groups', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/groups/delete/{id}', ['controller' => 'Groups', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/groups/handleCapabilities/{id}', ['controller' => 'Groups', 'action' => 'handleCapabilities'])->setPass(['id']);
$builder->connect('/maps', ['controller' => 'Maps', 'action' => 'index']);
$builder->connect('/maps/index', ['controller' => 'Maps', 'action' => 'index']);
$builder->connect('/maps/getWfs', ['controller' => 'Maps', 'action' => 'getWfs']);
$builder->connect('/maps/geocode', ['controller' => 'Maps', 'action' => 'geocode']);
$builder->connect('/mobile_phones/add/{id}', ['controller' => 'MobilePhones', 'action' => 'add'])->setPass(['id']);
$builder->connect('/mobile_phones/edit/{id}', ['controller' => 'MobilePhones', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/notifications', ['controller' => 'Notifications', 'action' => 'index']);
$builder->connect('/notifications/index', ['controller' => 'Notifications', 'action' => 'index']);
$builder->connect('/notifications/readAll', ['controller' => 'Notifications', 'action' => 'readAll']);
$builder->connect('/notifications/testAll', ['controller' => 'Notifications', 'action' => 'testAll']);
$builder->connect('/notifications/view/{id}', ['controller' => 'Notifications', 'action' => 'view'])->setPass(['id']);
$builder->connect('/organisations', ['controller' => 'Organisations', 'action' => 'index']);
$builder->connect('/organisations/index', ['controller' => 'Organisations', 'action' => 'index']);
$builder->connect('/organisations/add', ['controller' => 'Organisations', 'action' => 'add']);
$builder->connect('/organisations/view/{id}', ['controller' => 'Organisations', 'action' => 'view'])->setPass(['id']);
$builder->connect('/organisations/edit/{id}', ['controller' => 'Organisations', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/organisations/delete/{id}', ['controller' => 'Organisations', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/phones/add/{id}', ['controller' => 'Phones', 'action' => 'add'])->setPass(['id']);
$builder->connect('/phones/edit/{id}', ['controller' => 'Phones', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/telegram_chats/add/{id}', ['controller' => 'TelegramChats', 'action' => 'add'])->setPass(['id']);
$builder->connect('/telegram_chats/edit/{id}', ['controller' => 'TelegramChats', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/users', ['controller' => 'Users', 'action' => 'index']);
$builder->connect('/users/login_oidc', ['controller' => 'Users', 'action' => 'login_oidc']);
$builder->connect('/users/login_oidc_authenticated', ['controller' => 'Users', 'action' => 'login_oidc_authenticated']);
$builder->connect('/users/index', ['controller' => 'Users', 'action' => 'index']);
$builder->connect('/users/add', ['controller' => 'Users', 'action' => 'add']);
$builder->connect('/users/view/{id}', ['controller' => 'Users', 'action' => 'view'])->setPass(['id']);
$builder->connect('/users/edit/{id}', ['controller' => 'Users', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/users/delete/{id}', ['controller' => 'Users', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/users/login', ['controller' => 'Users', 'action' => 'login']);
$builder->connect('/users/logout', ['controller' => 'Users', 'action' => 'logout']);
$builder->connect('/users/passwordRecovery', ['controller' => 'Users', 'action' => 'passwordRecovery']);
$builder->connect('/users/chooseNewPassword/{id}', ['controller' => 'Users', 'action' => 'chooseNewPassword'])->setPass(['id']);
$builder->connect('/users/oneTimePassword/{otp}', ['controller' => 'Users', 'action' => 'oneTimePassword'])->setPass(['otp']);
$builder->connect('/users/getPhoto/{id}', ['controller' => 'Users', 'action' => 'getPhoto'])->setPass(['id']);
$builder->connect('/users/deleteUserPhoto/{id}', ['controller' => 'Users', 'action' => 'deleteUserPhoto'])->setPass(['id']);
$builder->connect('/users/add-citizen', ['controller' => 'Users', 'action' => 'add_citizen']);
$builder->connect('/users/verify-citizen/{token}', ['controller' => 'Users', 'action' => 'verify_citizen'])->setPass(['token']);
$builder->connect('/water_drawing_paperworks', ['controller' => 'WaterDrawingPaperworks', 'action' => 'index']);
$builder->connect('/water_drawing_paperworks/index', ['controller' => 'WaterDrawingPaperworks', 'action' => 'index']);
$builder->connect('/water_drawing_paperworks/index_snapshots/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'index_snapshots'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_paperworks/index_snapshots_all', ['controller' => 'WaterDrawingPaperworks', 'action' => 'index_snapshots_all']);
$builder->connect('/water_drawing_paperworks/validate_index', ['controller' => 'WaterDrawingPaperworks', 'action' => 'validate_index']);
$builder->connect('/water_drawing_paperworks/add_scan', ['controller' => 'WaterDrawingPaperworks', 'action' => 'add_scan']);
$builder->connect('/water_drawing_paperworks/add', ['controller' => 'WaterDrawingPaperworks', 'action' => 'add']);
$builder->connect('/water_drawing_paperworks/view_scan/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'view_scan'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/view/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'view'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/view_scan_snapshot/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'view_scan_snapshot'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/view_snapshot/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'view_snapshot'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/edit_scan/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'edit_scan'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/edit/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/delete/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/validate_scan/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'validate_scan'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/validate/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'validate'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/send_to_drar/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'send_to_drar'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/send_to_gc/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'send_to_gc'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/submit_antimafia_request/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'submit_antimafia_request'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/antimafia_request_to_anac/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'antimafia_request_to_anac'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/request_self_certification/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'request_self_certification'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/applicants_water_drawing_paperworks_item_block/{id}/{status_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'applicants_water_drawing_paperworks_item_block'])->setPass(['id', 'status_id']);
$builder->connect('/water_drawing_paperworks/applicants_water_drawing_paperworks_item_block/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'applicants_water_drawing_paperworks_item_block'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/intended_uses_water_drawing_paperworks_item_block/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'intended_uses_water_drawing_paperworks_item_block'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/upload_attachment/{id}/{tag_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'upload_attachment'])->setPass(['id', 'tag_id']);
$builder->connect('/water_drawing_paperworks/upload_attachment_scan/{id}/{tag_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'upload_attachment_scan'])->setPass(['id', 'tag_id']);
$builder->connect('/water_drawing_paperworks/upload_antimafia_attachment/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'upload_antimafia_attachment'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/assign/{id}/{organisation_type_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'assign'])->setPass(['id', 'organisation_type_id']);
$builder->connect('/water_drawing_paperworks/citizen_submission_index', ['controller' => 'WaterDrawingPaperworks', 'action' => 'citizen_submission_index']);
$builder->connect('/water_drawing_paperworks/citizen_documentation_index/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'citizen_documentation_index'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/citizen_documentation_requested/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'citizen_documentation_requested'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/citizen_upload_attachment/{id}/{tag_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'citizen_upload_attachment'])->setPass(['id', 'tag_id']);
$builder->connect('/water_drawing_paperworks/citizen_submit_paperwork/{applicant_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'citizen_submit_paperwork'])->setPass(['applicant_id']);
$builder->connect('/water_drawing_paperworks/citizen_request_paperwork', ['controller' => 'WaterDrawingPaperworks', 'action' => 'citizen_request_paperwork']);
$builder->connect('/water_drawing_paperworks/citizen_send_to_validation/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'citizen_send_to_validation'])->setPass(['id']);
$builder->connect('/water_drawing_paperworks/delete_attachment/{id}/{attachment_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'delete_attachment'])->setPass(['id', 'attachment_id']);
$builder->connect('/water_drawing_paperworks/send_to_validation/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'send_to_validation'])->setPass(['id']);
$builder->connect('/applicants/search_applicants', ['controller' => 'Applicants', 'action' => 'search_applicants']);
$builder->connect('/applicants/get_applicant/{id}', ['controller' => 'Applicants', 'action' => 'get_applicant'])->setPass(['id']);
$builder->connect('/applicants/edit/{id}', ['controller' => 'Applicants', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/applicants/edit/{id}/{water_id}', ['controller' => 'Applicants', 'action' => 'edit'])->setPass(['id','water_id']);
$builder->connect('/applicants/view/{id}/', ['controller' => 'Applicants', 'action' => 'view'])->setPass(['id']);
$builder->connect('/applicants/citizen_add/', ['controller' => 'Applicants', 'action' => 'citizen_add']);
$builder->connect('/applicants/citizen_edit/{id}', ['controller' => 'Applicants', 'action' => 'citizen_edit'])->setPass(['id']);
$builder->connect('/applicants/citizen_view', ['controller' => 'Applicants', 'action' => 'citizen_view']);
$builder->connect('/water_drawing_measurements/add/{water_drawing_meter_id}', ['controller' => 'WaterDrawingMeasurements', 'action' => 'add'])->setPass(['water_drawing_meter_id']);
$builder->connect('/water_drawing_measurements/index/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingMeasurements', 'action' => 'index'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_meters/add/{water_drawing_derivation_id}', ['controller' => 'WaterDrawingMeters', 'action' => 'add'])->setPass(['water_drawing_derivation_id']);
$builder->connect('/water_drawing_meters/edit/{water_drawing_meter_id}', ['controller' => 'WaterDrawingMeters', 'action' => 'edit'])->setPass(['water_drawing_meter_id']);
$builder->connect('/water_drawing_meters/view/{water_drawing_meter_id}', ['controller' => 'WaterDrawingMeters', 'action' => 'view'])->setPass(['water_drawing_meter_id']);
$builder->connect('/water_drawing_meters/dismiss/{water_drawing_meter_id}', ['controller' => 'WaterDrawingMeters', 'action' => 'dismiss'])->setPass(['water_drawing_meter_id']);
$builder->connect('/water_drawing_payments/index_all', ['controller' => 'WaterDrawingPayments', 'action' => 'index_all']);
$builder->connect('/water_drawing_payments/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingPayments', 'action' => 'index'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_payments/index/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingPayments', 'action' => 'index'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_payments/add/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingPayments', 'action' => 'add'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_payments/edit/{id}', ['controller' => 'WaterDrawingPayments', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/water_drawing_payments/view/{id}', ['controller' => 'WaterDrawingPayments', 'action' => 'view'])->setPass(['id']);
$builder->connect('/water_drawing_payments/delete/{id}', ['controller' => 'WaterDrawingPayments', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/water_drawing_derivations/add/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingDerivations', 'action' => 'add'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_derivations/view/{water_drawing_derivation_id}', ['controller' => 'WaterDrawingDerivations', 'action' => 'view'])->setPass(['water_drawing_derivation_id']);
$builder->connect('/water_drawing_derivations/edit/{water_drawing_derivation_id}', ['controller' => 'WaterDrawingDerivations', 'action' => 'edit'])->setPass(['water_drawing_derivation_id']);
$builder->connect('/water_drawing_derivations/delete/{water_drawing_derivation_id}', ['controller' => 'WaterDrawingDerivations', 'action' => 'delete'])->setPass(['water_drawing_derivation_id']);
$builder->connect('/water_drawing_return_points/add/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingReturnPoints', 'action' => 'add'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_return_points/view/{id}', ['controller' => 'WaterDrawingReturnPoints', 'action' => 'view'])->setPass(['id']);
$builder->connect('/water_drawing_return_points/edit/{id}', ['controller' => 'WaterDrawingReturnPoints', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/water_drawing_return_points/delete/{id}', ['controller' => 'WaterDrawingReturnPoints', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/water_drawing_articles/', ['controller' => 'WaterDrawingArticles', 'action' => 'index']);
$builder->connect('/water_drawing_articles/add', ['controller' => 'WaterDrawingArticles', 'action' => 'add']);
$builder->connect('/water_drawing_articles/edit/{id}', ['controller' => 'WaterDrawingArticles', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/water_drawing_articles/view/{id}', ['controller' => 'WaterDrawingArticles', 'action' => 'view'])->setPass(['id']);
$builder->connect('/water_drawing_articles/delete/{id}', ['controller' => 'WaterDrawingArticles', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/water_drawing_articles/change_status/{id}', ['controller' => 'WaterDrawingArticles', 'action' => 'change_status'])->setPass(['id']);
$builder->connect('/water_drawing_fees/index/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingFees', 'action' => 'index'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_fees/add/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingFees', 'action' => 'add'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_fees/view/{id}', ['controller' => 'WaterDrawingFees', 'action' => 'view'])->setPass(['id']);
$builder->connect('/water_drawing_fees/edit/{id}', ['controller' => 'WaterDrawingFees', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/water_drawing_fees/delete/{id}', ['controller' => 'WaterDrawingFees', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/water_drawing_paperwork_pecs/index/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingPaperworkPecs', 'action' => 'index'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_paperwork_pecs/add/{water_drawing_paperwork_id}', ['controller' => 'WaterDrawingPaperworkPecs', 'action' => 'add'])->setPass(['water_drawing_paperwork_id']);
$builder->connect('/water_drawing_paperwork_pecs/edit/{id}', ['controller' => 'WaterDrawingPaperworkPecs', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/water_drawing_paperwork_pecs/view/{id}', ['controller' => 'WaterDrawingPaperworkPecs', 'action' => 'view'])->setPass(['id']);
$builder->connect('/water_drawing_paperwork_pecs/delete/{id}', ['controller' => 'WaterDrawingPaperworkPecs', 'action' => 'delete'])->setPass(['id']);
$builder->connect('/ai_services/autocomplete', ['controller' => 'AiServices', 'action' => 'autocomplete']);
$builder->connect('/pecs/add/{id}', ['controller' => 'Pecs', 'action' => 'add'])->setPass(['id']);
$builder->connect('/pecs/edit/{id}', ['controller' => 'Pecs', 'action' => 'edit'])->setPass(['id']);
$builder->connect('/privacy/edit/', ['controller' => 'Privacy', 'action' => 'edit']);
$builder->connect('/privacy/view', ['controller' => 'Privacy', 'action' => 'view']);
$builder->connect('/privacy/view_public', ['controller' => 'Privacy', 'action' => 'view_public']);
$builder->connect('/privacy/accept_privacy', ['controller' => 'Privacy', 'action' => 'accept_privacy']);
$builder->connect('/districts/search_districts', ['controller' => 'Districts', 'action' => 'search_districts']);
$builder->connect('/water_drawing_paperworks/citizen_upload_payment_receipt/{id}/{tag_id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'citizen_upload_payment_receipt'])->setPass(['id', 'tag_id']);
$builder->connect('/water_drawing_paperworks/index_csv', ['controller' => 'WaterDrawingPaperworks', 'action' => 'index_csv']);
$builder->connect('/water_drawing_paperworks/index_snapshots_all_csv', ['controller' => 'WaterDrawingPaperworks', 'action' => 'index_snapshots_all_csv']);
$builder->connect('/water_drawing_paperworks/index_snapshots_csv/{id}', ['controller' => 'WaterDrawingPaperworks', 'action' => 'index_snapshots_csv'])->setPass(['id']);
$builder->connect('/tags', ['controller' => 'Tags', 'action' => 'index']);
$builder->connect('/tags/index', ['controller' => 'Tags', 'action' => 'index']);
$builder->connect('/tags/add', ['controller' => 'Tags', 'action' => 'add']);
$builder->connect('/tags/view', ['controller' => 'Tags', 'action' => 'view']);
$builder->connect('/tags/view/{id}', ['controller' => 'Tags', 'action' => 'view'])->setPass(['id']);
$builder->connect('/tags/edit', ['controller' => 'Tags', 'action' => 'edit']);
$builder->connect('/tags/edit/{id}', ['controller' => 'Tags', 'action' => 'edit'])->setPass(['id']);
});
};

18
idrocap_wa/config/schema/i18n.sql

@ -0,0 +1,18 @@
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE i18n (
id int NOT NULL auto_increment,
locale varchar(6) NOT NULL,
model varchar(255) NOT NULL,
foreign_key int(10) NOT NULL,
field varchar(255) NOT NULL,
content text,
PRIMARY KEY (id),
UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field),
INDEX I18N_FIELD(model, foreign_key, field)
);

15
idrocap_wa/config/schema/sessions.sql

@ -0,0 +1,15 @@
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE `sessions` (
`id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`created` datetime DEFAULT CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
`modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
`data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob
`expires` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

152
idrocap_wa/dockerfiles/cake

@ -0,0 +1,152 @@
#!/usr/bin/env sh
if [ -f /run/secrets/sentry_dsn ]; then
export SENTRY_DSN=$(cat /run/secrets/sentry_dsn)
else
echo "Secret file 'sentry_dsn' not found!" >&2
fi
if [ -f /run/secrets/oidc_client_id ]; then
export OIDC_CLIENT_ID=$(cat /run/secrets/oidc_client_id)
else
echo "Secret file 'oidc_client_id' not found!" >&2
fi
if [ -f /run/secrets/rabbitmq_username ]; then
export RABBITMQ_USERNAME=$(cat /run/secrets/rabbitmq_username)
else
echo "Secret file 'rabbitmq_username' not found!" >&2
fi
if [ -f /run/secrets/rabbitmq_password ]; then
export RABBITMQ_PASSWORD=$(cat /run/secrets/rabbitmq_password)
else
echo "Secret file 'rabbitmq_password' not found!" >&2
fi
if [ -f /run/secrets/geoserver_username ]; then
export GEOSERVER_USERNAME=$(cat /run/secrets/geoserver_username)
else
echo "Secret file 'geoserver_username' not found!" >&2
fi
if [ -f /run/secrets/geoserver_password ]; then
export GEOSERVER_PASSWORD=$(cat /run/secrets/geoserver_password)
else
echo "Secret file 'geoserver_password' not found!" >&2
fi
if [ -f /run/secrets/minio_access_key ]; then
export MINIO_ACCESS_KEY=$(cat /run/secrets/minio_access_key)
else
echo "Secret file 'minio_access_key' not found!" >&2
fi
if [ -f /run/secrets/minio_secret_key ]; then
export MINIO_SECRET_KEY=$(cat /run/secrets/minio_secret_key)
else
echo "Secret file 'minio_secret_key' not found!" >&2
fi
if [ -f /run/secrets/telegram_bot_token ]; then
export TELEGRAM_BOT_TOKEN=$(cat /run/secrets/telegram_bot_token)
else
echo "Secret file 'telegram_bot_token' not found!" >&2
fi
if [ -f /run/secrets/security_jwuc ]; then
export SECURITY_JWUC=$(cat /run/secrets/security_jwuc)
else
echo "Secret file 'security_jwuc' not found!" >&2
fi
if [ -f /run/secrets/security_jwac ]; then
export SECURITY_JWAC=$(cat /run/secrets/security_jwac)
else
echo "Secret file 'security_jwac' not found!" >&2
fi
if [ -f /run/secrets/mysql_jixel_username ]; then
export MYSQL_JIXEL_USERNAME=$(cat /run/secrets/mysql_jixel_username)
else
echo "Secret file 'mysql_jixel_username' not found!" >&2
fi
if [ -f /run/secrets/mysql_jixel_password ]; then
export MYSQL_JIXEL_PASSWORD=$(cat /run/secrets/mysql_jixel_password)
else
echo "Secret file 'mysql_jixel_password' not found!" >&2
fi
if [ -f /run/secrets/mysql_geo_jixel_username ]; then
export MYSQL_GEO_JIXEL_USERNAME=$(cat /run/secrets/mysql_geo_jixel_username)
else
echo "Secret file 'mysql_geo_jixel_username' not found!" >&2
fi
if [ -f /run/secrets/mysql_geo_jixel_password ]; then
export MYSQL_GEO_JIXEL_PASSWORD=$(cat /run/secrets/mysql_geo_jixel_password)
else
echo "Secret file 'mysql_geo_jixel_password' not found!" >&2
fi
################################################################################
#
# Cake is a shell script for invoking CakePHP shell commands
#
# CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
#
# @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
# @link https://cakephp.org CakePHP(tm) Project
# @since 1.2.0
# @license https://opensource.org/licenses/mit-license.php MIT License
#
################################################################################
# Canonicalize by following every symlink of the given name recursively
canonicalize() {
NAME="$1"
if [ -f "$NAME" ]
then
DIR=$(dirname -- "$NAME")
NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME")
fi
while [ -h "$NAME" ]; do
DIR=$(dirname -- "$NAME")
SYM=$(readlink "$NAME")
NAME=$(cd "$DIR" > /dev/null && cd "$(dirname -- "$SYM")" > /dev/null && pwd)/$(basename -- "$SYM")
done
echo "$NAME"
}
# Find a CLI version of PHP
findCliPhp() {
for TESTEXEC in php php-cli /usr/local/bin/php
do
SAPI=$(echo "<?= PHP_SAPI ?>" | $TESTEXEC 2>/dev/null)
if [ "$SAPI" = "cli" ]
then
echo $TESTEXEC
return
fi
done
echo "Failed to find a CLI version of PHP; falling back to system standard php executable" >&2
echo "php";
}
# If current path is a symlink, resolve to real path
realname="$0"
if [ -L "$realname" ]
then
realname=$(readlink -f "$0")
fi
CONSOLE=$(dirname -- "$(canonicalize "$realname")")
APP=$(dirname "$CONSOLE")
# If your CLI PHP is somewhere that this doesn't find, you can define a PHP environment
# variable with the correct path in it.
if [ -z "$PHP" ]
then
PHP=$(findCliPhp)
fi
if [ "$(basename "$realname")" != 'cake' ]
then
exec "$PHP" "$CONSOLE"/cake.php "$(basename "$realname")" "$@"
else
exec "$PHP" "$CONSOLE"/cake.php "$@"
fi
exit

93
idrocap_wa/dockerfiles/run

@ -0,0 +1,93 @@
#!/bin/bash
httpd_user=${httpd_user:-apache}
httpd_uid=${httpd_uid:-48}
httpd_group=${httpd_group:-apache}
useradd -u $httpd_uid -s /sbin/nologin $httpd_user
export httpd_user
export httpd_group
if [ -f /run/secrets/sentry_dsn ]; then
export SENTRY_DSN=$(cat /run/secrets/sentry_dsn)
else
echo "Secret file 'sentry_dsn' not found!" >&2
fi
if [ -f /run/secrets/oidc_client_id ]; then
export OIDC_CLIENT_ID=$(cat /run/secrets/oidc_client_id)
else
echo "Secret file 'oidc_client_id' not found!" >&2
fi
if [ -f /run/secrets/rabbitmq_username ]; then
export RABBITMQ_USERNAME=$(cat /run/secrets/rabbitmq_username)
else
echo "Secret file 'rabbitmq_username' not found!" >&2
fi
if [ -f /run/secrets/rabbitmq_password ]; then
export RABBITMQ_PASSWORD=$(cat /run/secrets/rabbitmq_password)
else
echo "Secret file 'rabbitmq_password' not found!" >&2
fi
if [ -f /run/secrets/geoserver_username ]; then
export GEOSERVER_USERNAME=$(cat /run/secrets/geoserver_username)
else
echo "Secret file 'geoserver_username' not found!" >&2
fi
if [ -f /run/secrets/geoserver_password ]; then
export GEOSERVER_PASSWORD=$(cat /run/secrets/geoserver_password)
else
echo "Secret file 'geoserver_password' not found!" >&2
fi
if [ -f /run/secrets/minio_access_key ]; then
export MINIO_ACCESS_KEY=$(cat /run/secrets/minio_access_key)
else
echo "Secret file 'minio_access_key' not found!" >&2
fi
if [ -f /run/secrets/minio_secret_key ]; then
export MINIO_SECRET_KEY=$(cat /run/secrets/minio_secret_key)
else
echo "Secret file 'minio_secret_key' not found!" >&2
fi
if [ -f /run/secrets/telegram_bot_token ]; then
export TELEGRAM_BOT_TOKEN=$(cat /run/secrets/telegram_bot_token)
else
echo "Secret file 'telegram_bot_token' not found!" >&2
fi
if [ -f /run/secrets/security_jwuc ]; then
export SECURITY_JWUC=$(cat /run/secrets/security_jwuc)
else
echo "Secret file 'security_jwuc' not found!" >&2
fi
if [ -f /run/secrets/security_jwac ]; then
export SECURITY_JWAC=$(cat /run/secrets/security_jwac)
else
echo "Secret file 'security_jwac' not found!" >&2
fi
if [ -f /run/secrets/mysql_jixel_username ]; then
export MYSQL_JIXEL_USERNAME=$(cat /run/secrets/mysql_jixel_username)
else
echo "Secret file 'mysql_jixel_username' not found!" >&2
fi
if [ -f /run/secrets/mysql_jixel_password ]; then
export MYSQL_JIXEL_PASSWORD=$(cat /run/secrets/mysql_jixel_password)
else
echo "Secret file 'mysql_jixel_password' not found!" >&2
fi
if [ -f /run/secrets/mysql_geo_jixel_username ]; then
export MYSQL_GEO_JIXEL_USERNAME=$(cat /run/secrets/mysql_geo_jixel_username)
else
echo "Secret file 'mysql_geo_jixel_username' not found!" >&2
fi
if [ -f /run/secrets/mysql_geo_jixel_password ]; then
export MYSQL_GEO_JIXEL_PASSWORD=$(cat /run/secrets/mysql_geo_jixel_password)
else
echo "Secret file 'mysql_geo_jixel_password' not found!" >&2
fi
chmod -R o+w /var/www/jixel/tmp/
chmod -R o+w /var/www/jixel/logs/
/usr/sbin/php-fpm
chown -R $httpd_user:$httpd_group /run/php-fpm
/usr/sbin/httpd -DFOREGROUND

453
idrocap_wa/dockerfiles/www.conf

@ -0,0 +1,453 @@
; Start a new pool named 'www'.
; the variable $pool can be used in any directive and will be replaced by the
; pool name ('www' here)
[www]
; Per pool prefix
; It only applies on the following directives:
; - 'access.log'
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; When not set, the global prefix (or @php_fpm_prefix@) applies instead.
; Note: This directive can also be relative to the global prefix.
; Default Value: none
;prefix = /path/to/pools/$pool
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
; will be used.
; RPM: apache user chosen to provide access to the same directories as httpd
user = apache
; RPM: Keep a group allowed to write in log dir.
group = apache
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
; a specific port;
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
; a specific port;
; 'port' - to listen on a TCP socket to all addresses
; (IPv6 and IPv4-mapped) on a specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /run/php-fpm/www.sock
; Set listen(2) backlog.
; Default Value: 511
;listen.backlog = 511
; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server.
; Default Values: user and group are set as the running user
; mode is set to 0660
;listen.owner = nobody
;listen.group = nobody
;listen.mode = 0660
; When POSIX Access Control Lists are supported you can set them using
; these options, value is a comma separated list of user/group names.
; When set, listen.owner and listen.group are ignored
listen.acl_users = apache,nginx
;listen.acl_groups =
; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
; must be separated by a comma. If this value is left blank, connections will be
; accepted from any ip address.
; Default Value: any
listen.allowed_clients = 127.0.0.1
; Specify the nice(2) priority to apply to the pool processes (only if set)
; The value can vary from -19 (highest priority) to 20 (lower priority)
; Note: - It will only work if the FPM master process is launched as root
; - The pool processes will inherit the master process priority
; unless it specified otherwise
; Default Value: no set
; process.priority = -19
; Set the process dumpable flag (PR_SET_DUMPABLE prctl) even if the process user
; or group is differrent than the master process user. It allows to create process
; core dump and ptrace the process for the pool user.
; Default Value: no
; process.dumpable = yes
; Choose how the process manager will control the number of child processes.
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives. With this process management, there will be
; always at least 1 children.
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
; pm.min_spare_servers - the minimum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is less than this
; number then some children will be created.
; pm.max_spare_servers - the maximum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; ondemand - no children are created at startup. Children will be forked when
; new requests will connect. The following parameter are used:
; pm.max_children - the maximum number of children that
; can be alive at the same time.
; pm.process_idle_timeout - The number of seconds after which
; an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 50
; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 5
; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 5
; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 35
; The number of seconds after which an idle process will be killed.
; Note: Used only when pm is set to 'ondemand'
; Default Value: 10s
;pm.process_idle_timeout = 10s;
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
;pm.max_requests = 500
; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. It shows the following informations:
; pool - the name of the pool;
; process manager - static, dynamic or ondemand;
; start time - the date and time FPM has started;
; start since - number of seconds since FPM has started;
; accepted conn - the number of request accepted by the pool;
; listen queue - the number of request in the queue of pending
; connections (see backlog in listen(2));
; max listen queue - the maximum number of requests in the queue
; of pending connections since FPM has started;
; listen queue len - the size of the socket queue of pending connections;
; idle processes - the number of idle processes;
; active processes - the number of active processes;
; total processes - the number of idle + active processes;
; max active processes - the maximum number of active processes since FPM
; has started;
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
; pm 'dynamic' and 'ondemand');
; Value are updated in real time.
; Example output:
; pool: www
; process manager: static
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 62636
; accepted conn: 190460
; listen queue: 0
; max listen queue: 1
; listen queue len: 42
; idle processes: 4
; active processes: 11
; total processes: 15
; max active processes: 12
; max children reached: 0
;
; By default the status page output is formatted as text/plain. Passing either
; 'html', 'xml' or 'json' in the query string will return the corresponding
; output syntax. Example:
; http://www.foo.bar/status
; http://www.foo.bar/status?json
; http://www.foo.bar/status?html
; http://www.foo.bar/status?xml
;
; By default the status page only outputs short status. Passing 'full' in the
; query string will also return status for each pool process.
; Example:
; http://www.foo.bar/status?full
; http://www.foo.bar/status?json&full
; http://www.foo.bar/status?html&full
; http://www.foo.bar/status?xml&full
; The Full status returns for each process:
; pid - the PID of the process;
; state - the state of the process (Idle, Running, ...);
; start time - the date and time the process has started;
; start since - the number of seconds since the process has started;
; requests - the number of requests the process has served;
; request duration - the duration in µs of the requests;
; request method - the request method (GET, POST, ...);
; request URI - the request URI with the query string;
; content length - the content length of the request (only with POST);
; user - the user (PHP_AUTH_USER) (or '-' if not set);
; script - the main script called (or '-' if not set);
; last request cpu - the %cpu the last request consumed
; it's always 0 if the process is not in Idle state
; because CPU calculation is done when the request
; processing has terminated;
; last request memory - the max amount of memory the last request consumed
; it's always 0 if the process is not in Idle state
; because memory calculation is done when the request
; processing has terminated;
; If the process is in Idle state, then informations are related to the
; last request the process has served. Otherwise informations are related to
; the current request being served.
; Example output:
; ************************
; pid: 31330
; state: Running
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 63087
; requests: 12808
; request duration: 1250261
; request method: GET
; request URI: /test_mem.php?N=10000
; content length: 0
; user: -
; script: /home/fat/web/docs/php/test_mem.php
; last request cpu: 0.00
; last request memory: 0
;
; Note: There is a real-time FPM status monitoring sample web page available
; It's available in: @EXPANDED_DATADIR@/fpm/status.html
;
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;pm.status_path = /status
; The ping URI to call the monitoring page of FPM. If this value is not set, no
; URI will be recognized as a ping page. This could be used to test from outside
; that FPM is alive and responding, or to
; - create a graph of FPM availability (rrd or such);
; - remove a server from a group if it is not responding (load balancing);
; - trigger alerts for the operating team (24/7).
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;ping.path = /ping
; This directive may be used to customize the response of a ping request. The
; response is formatted as text/plain with a 200 response code.
; Default Value: pong
;ping.response = pong
; The access log file
; Default: not set
;access.log = log/$pool.access.log
; The access log format.
; The following syntax is allowed
; %%: the '%' character
; %C: %CPU used by the request
; it can accept the following format:
; - %{user}C for user CPU only
; - %{system}C for system CPU only
; - %{total}C for user + system CPU (default)
; %d: time taken to serve the request
; it can accept the following format:
; - %{seconds}d (default)
; - %{miliseconds}d
; - %{mili}d
; - %{microseconds}d
; - %{micro}d
; %e: an environment variable (same as $_ENV or $_SERVER)
; it must be associated with embraces to specify the name of the env
; variable. Some exemples:
; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
; %f: script filename
; %l: content-length of the request (for POST request only)
; %m: request method
; %M: peak of memory allocated by PHP
; it can accept the following format:
; - %{bytes}M (default)
; - %{kilobytes}M
; - %{kilo}M
; - %{megabytes}M
; - %{mega}M
; %n: pool name
; %o: output header
; it must be associated with embraces to specify the name of the header:
; - %{Content-Type}o
; - %{X-Powered-By}o
; - %{Transfert-Encoding}o
; - ....
; %p: PID of the child that serviced the request
; %P: PID of the parent of the child that serviced the request
; %q: the query string
; %Q: the '?' character if query string exists
; %r: the request URI (without the query string, see %q and %Q)
; %R: remote IP address
; %s: status (response code)
; %t: server time the request was received
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; The strftime(3) format must be encapsuled in a %{<strftime_format>}t tag
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
; %T: time the log has been written (the request has finished)
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; The strftime(3) format must be encapsuled in a %{<strftime_format>}t tag
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
; %u: remote user
;
; Default: "%R - %u %t \"%m %r\" %s"
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
; The log file for slow requests
; Default Value: not set
; Note: slowlog is mandatory if request_slowlog_timeout is set
slowlog = /var/log/php-fpm/www-slow.log
; The timeout for serving a single request after which a PHP backtrace will be
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_slowlog_timeout = 0
; Depth of slow log stack trace.
; Default Value: 20
;request_slowlog_trace_depth = 20
; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_terminate_timeout = 0
; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024
; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0
; Chroot to this directory at the start. This value must be defined as an
; absolute path. When this value is not set, chroot is not used.
; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
; of its subdirectories. If the pool prefix is not set, the global prefix
; will be used instead.
; Note: chrooting is a great security feature and should be used whenever
; possible. However, all PHP paths will be relative to the chroot
; (error_log, sessions.save_path, ...).
; Default Value: not set
;chroot =
; Chdir to this directory at the start.
; Note: relative path can be used.
; Default Value: current directory or / when chroot
;chdir = /var/www
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Note: on highloaded environement, this can cause some delay in the page
; process time (several ms).
; Default Value: no
;catch_workers_output = yes
; Clear environment in FPM workers
; Prevents arbitrary environment variables from reaching FPM worker processes
; by clearing the environment in workers before env vars specified in this
; pool configuration are added.
; Setting to "no" will make all environment variables available to PHP code
; via getenv(), $_ENV and $_SERVER.
; Default Value: yes
; clear_env = no
; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; execute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
;security.limit_extensions = .php .php3 .php4 .php5 .php7
; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
; the current environment.
; Default Value: clean env
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
env[SENTRY_DSN] = "${SENTRY_DSN}"
env[OIDC_CLIENT_ID] = "${OIDC_CLIENT_ID}"
env[RABBITMQ_USERNAME] = "${RABBITMQ_USERNAME}"
env[RABBITMQ_PASSWORD] = "${RABBITMQ_PASSWORD}"
env[GEOSERVER_USERNAME] = "${GEOSERVER_USERNAME}"
env[GEOSERVER_PASSWORD] = "${GEOSERVER_PASSWORD}"
env[MINIO_ACCESS_KEY] = "${MINIO_ACCESS_KEY}"
env[MINIO_SECRET_KEY] = "${MINIO_SECRET_KEY}"
env[TELEGRAM_BOT_TOKEN] = "${TELEGRAM_BOT_TOKEN}"
env[SECURITY_JWUC] = "${SECURITY_JWUC}"
env[SECURITY_JWAC] = "${SECURITY_JWAC}"
env[MYSQL_JIXEL_USERNAME] = "${MYSQL_JIXEL_USERNAME}"
env[MYSQL_JIXEL_PASSWORD] = "${MYSQL_JIXEL_PASSWORD}"
env[MYSQL_GEO_JIXEL_USERNAME] = "${MYSQL_GEO_JIXEL_USERNAME}"
env[MYSQL_GEO_JIXEL_PASSWORD] = "${MYSQL_GEO_JIXEL_PASSWORD}"
; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.
; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or @prefix@)
; Default Value: nothing is defined by default except the values in php.ini and
; specified at startup with the -d argument
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 128M
; Set the following data paths to directories owned by the FPM process user.
;
; Do not change the ownership of existing system directories, if the process
; user does not have write permission, create dedicated directories for this
; purpose.
;
; See warning about choosing the location of these directories on your system
; at http://php.net/session.save-path
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session
php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache
;php_value[opcache.file_cache] = /var/lib/php/opcache

16
idrocap_wa/index.php

@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';

39
idrocap_wa/jixel-background-task-handler/Dockerfile

@ -0,0 +1,39 @@
ARG version
ARG branch=master
FROM registrywgs.webgenesis.it/webgenesys/jixel/idrocapjixel:$version-$branch
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN echo $branch
RUN echo $version
RUN mkdir /usr/local/ies-solutions
RUN mkdir /usr/local/ies-solutions/jixel-cron/
COPY ./jixel-cron.* /usr/local/ies-solutions/jixel-cron/
RUN mkdir /usr/local/ies-solutions/jixel-background-task-handler/
COPY ./jbth.* /usr/local/ies-solutions/jixel-background-task-handler/
RUN mkdir /usr/local/ies-solutions/jixel-general-purpose-rabbit-consumer/
COPY ./jgprc.* /usr/local/ies-solutions/jixel-general-purpose-rabbit-consumer/
WORKDIR /usr/local/ies-solutions/jixel-background-task-handler
RUN dnf -y install python3-pip python3-devel python3-pycurl
RUN dnf -y install crontabs cronie cronie-anacron
RUN pip3 install pika
RUN pip3 install rasterio
RUN pip3 install redis
COPY ./jixel-background-task-handler.sh /usr/local/bin/jixel-background-task-handler
COPY ./jixel-cron.sh /usr/local/bin/jixel-cron
COPY ./jixel-general-purpose-rabbit-consumer.sh /usr/local/bin/jixel-general-purpose-rabbit-consumer
RUN chmod 500 /usr/local/bin/jixel-background-task-handler
RUN chmod 500 /usr/local/bin/jixel-cron
RUN chmod 500 /usr/local/bin/jixel-general-purpose-rabbit-consumer
CMD ["jixel-background-task-handler"]

152
idrocap_wa/jixel-background-task-handler/jbth.py

@ -0,0 +1,152 @@
import logging
import time
import json
import pika
import subprocess
import sys
import threading
import os
from distutils.util import strtobool
class JixelBackgroundTaskHandler():
def __init__(
self, rabbitmq_username, rabbitmq_password,
rabbitmq_host, rabbitmq_port, rabbitmq_queue,
rabbitmq_heartbeat, execution_path, logger,
discarding_mode=False
):
self.rabbitmq_username = rabbitmq_username
self.rabbitmq_password = rabbitmq_password
self.rabbitmq_host = rabbitmq_host
self.rabbitmq_port = rabbitmq_port
self.rabbitmq_queue = rabbitmq_queue
self.rabbitmq_heartbeat = int(rabbitmq_heartbeat)
self.execution_path = execution_path
self.logger = logger
self.discarding_mode = strtobool(discarding_mode)
self.logger.info("JixelBackgroundTaskHandler - Initialization...")
self.credentials = pika.PlainCredentials(
self.rabbitmq_username ,
self.rabbitmq_password
)
self.connection = pika.BlockingConnection(
pika.ConnectionParameters(
host=self.rabbitmq_host,
port=int(self.rabbitmq_port),
credentials=self.credentials,heartbeat=self.rabbitmq_heartbeat
)
)
self.channel = self.connection.channel()
self.logger.info("JixelBackgroundTaskHandler - Initialization completed successfully")
self.channel.queue_declare(queue=self.rabbitmq_queue, durable=True)
self.logger.info("JixelBackgroundTaskHandler - " + self.rabbitmq_queue + " queue declared")
self.channel.queue_declare(queue=self.rabbitmq_queue+'_errors', durable=True)
self.logger.info("JixelBackgroundTaskHandler - " + self.rabbitmq_queue + "_errors queue declared")
self.channel.basic_qos(prefetch_count=1)
self.logger.info("JixelBackgroundTaskHandler - Queue handler declared")
self.channel.basic_consume(
on_message_callback=self.__data_handler,
queue=self.rabbitmq_queue
)
def consume(self):
try:
self.channel.start_consuming()
except KeyboardInterrupt:
self.channel.stop_consuming()
self.channel.close()
def __execute_command(self, mr):
self.logger.info('JixelBackgroundTaskHandler - Processing thread is running:')
command = [self.execution_path + 'cake']
command += mr['command'].split(' ')
command += [mr['data']]
self.logger.info('JixelBackgroundTaskHandler - Executing shell command:')
self.logger.info(command)
try:
subprocess.check_call(command)
self.result = True
except subprocess.CalledProcessError as error:
self.logger.error('JixelBackgroundTaskHandler - An error as occured during processing:')
self.logger.error(error)
self.result = False
def __data_processing(self, channel, mr):
self.logger.info(mr)
self.logger.info('JixelBackgroundTaskHandler - Starting processing thread:')
self.result = None
thread = threading.Thread(target=self.__execute_command, args=(mr,))
thread.start()
while thread.is_alive():
self.connection.process_data_events()
self.logger.info("JixelBackgroundTaskHandler - Result received:")
self.logger.info(self.result)
if (self.result is not True):
self.__send_error(json.dumps(mr))
return self.result
def __send_ack_nack(self, ack, channel, method):
if ack is True:
self.logger.info("JixelBackgroundTaskHandler - Ack performed")
channel.basic_ack(delivery_tag=method.delivery_tag)
else:
self.logger.info("JixelBackgroundTaskHandler - Nack performed BECAUSE ERRORS OCCURRED!")
channel.basic_reject(delivery_tag=method.delivery_tag)
def __data_handler(self, channel, method, properties, body):
self.logger.info('JixelBackgroundTaskHandler - New message received:')
error = False
try:
mr = json.loads(body.decode())
error = not self.__data_processing(channel, mr)
except json.JSONDecodeError as error:
self.logger.error("JixelBackgroundTaskHandler - Malformed message")
self.logger.error(error)
self.logger.error("JixelBackgroundTaskHandler - This message will be removed from the queue")
self.__send_ack_nack(False, channel, method)
time.sleep(60)
return
if self.result is True and error is False:
self.__send_ack_nack(True, channel, method)
else:
self.logger.error("JixelBackgroundTaskHandler - An error has occured during processing")
if self.discarding_mode is True:
self.logger.error("JixelBackgroundTaskHandler - Discarding mode enabled.")
self.__send_ack_nack(True, channel, method)
else:
self.__send_ack_nack(False, channel, method)
time.sleep(5)
self.logger.info("JixelBackgroundTaskHandler - Data handling complete. Waiting for new messages")
def __send_error(self, message):
self.channel.basic_publish(exchange='', routing_key=self.rabbitmq_queue+'_errors', body=message)
if __name__ == '__main__':
logger = logging.getLogger("JixelBackgroundTaskHandler")
logger.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
terminal_log = logging.StreamHandler(sys.stdout)
terminal_log.setFormatter(formatter)
logger.addHandler(terminal_log)
app = JixelBackgroundTaskHandler(
os.environ['RABBITMQ_USERNAME'],
os.environ['RABBITMQ_PASSWORD'],
os.environ['rabbitmq_host'],
os.environ['rabbitmq_port'],
os.environ['rabbitmq_queue'],
os.environ['rabbitmq_heartbeat'],
os.environ['execution_path'],
logger,
os.environ['discarding_mode']
)
app.consume()

9
idrocap_wa/jixel-background-task-handler/jbth.sh

@ -0,0 +1,9 @@
export rabbitmq_username='jixel'
export rabbitmq_password='jixel'
export rabbitmq_host='rabbitmq'
export rabbitmq_queue='background_tasks'
export rabbitmq_port='5672'
export execution_path='/var/www/gecos/bin/'
export rabbitmq_heartbeat=60
export discarding_mode='False'
python3 ./jbth.py

169
idrocap_wa/jixel-background-task-handler/jgprc.py

@ -0,0 +1,169 @@
import logging
import logging.handlers
import time
import json
import pika
import subprocess
import sys
import threading
import os
from distutils.util import strtobool
import shlex
class JixelGeneralPurposeRabbitConsumer():
def __init__(
self, rabbitmq_username, rabbitmq_password,
rabbitmq_host, rabbitmq_vhost, rabbitmq_port, rabbitmq_queue,
rabbitmq_heartbeat, execution_path, execution_command, logger,
discarding_mode=False
):
self.rabbitmq_username = rabbitmq_username
self.rabbitmq_password = rabbitmq_password
self.rabbitmq_host = rabbitmq_host
self.rabbitmq_vhost = rabbitmq_vhost
self.rabbitmq_port = rabbitmq_port
self.rabbitmq_queue = rabbitmq_queue
self.rabbitmq_heartbeat = int(rabbitmq_heartbeat)
self.execution_path = execution_path
self.execution_command = execution_command
self.logger = logger
self.discarding_mode = strtobool(discarding_mode)
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Initialization...")
self.credentials = pika.PlainCredentials(
self.rabbitmq_username ,
self.rabbitmq_password
)
self.connection = pika.BlockingConnection(
pika.ConnectionParameters(
host=self.rabbitmq_host,
virtual_host=self.rabbitmq_vhost,
port=int(self.rabbitmq_port),
credentials=self.credentials,heartbeat=self.rabbitmq_heartbeat
)
)
self.channel = self.connection.channel()
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Initialization completed successfully")
self.channel.queue_declare(queue=self.rabbitmq_queue, durable=True)
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - " + self.rabbitmq_queue + " queue declared")
self.channel.queue_declare(queue=self.rabbitmq_queue+'_errors', durable=True)
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - " + self.rabbitmq_queue + "_errors queue declared")
self.channel.basic_qos(prefetch_count=1)
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Queue handler declared")
self.channel.basic_consume(
on_message_callback=self.__data_handler,
queue=self.rabbitmq_queue
)
def consume(self):
try:
self.channel.start_consuming()
except KeyboardInterrupt:
self.channel.stop_consuming()
self.channel.close()
def __execute_command(self, mr):
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + ' - Processing thread is running:')
string_command = "{}cake {} '{}'".format(self.execution_path, self.execution_command, json.dumps(mr))
command = shlex.split(string_command)
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + ' - Executing shell command:')
self.logger.info(command)
try:
completed_process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if completed_process.stderr:
self.logger.error(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " -> " + self.execution_command + ':\n{}'.format(completed_process.stderr.decode('utf-8')))
if completed_process.stdout:
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " -> " + self.execution_command + ':\n{}'.format(completed_process.stdout.decode('utf-8')))
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " -> " + self.execution_command + ' - RETURN CODE: {}'.format(completed_process.returncode))
completed_process.check_returncode()
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " -> " + self.execution_command + ': Shell command successfully executed!')
self.result = True
except FileNotFoundError as error:
self.logger.exception(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " -> " + self.execution_command + ' - A FileNotFoundError error has occured during shell command execution:', stack_info=False, exc_info=False)
self.logger.exception(error, stack_info=False, exc_info=False)
return False
except subprocess.CalledProcessError as error:
self.logger.exception(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " -> " + self.execution_command + ' - A CalledProcessError has occured during processing:', stack_info=False, exc_info=False)
self.logger.exception(error, stack_info=False, exc_info=False)
self.result = False
def __data_processing(self, channel, mr):
self.logger.info(mr)
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + ' - Starting processing thread:')
self.result = None
thread = threading.Thread(target=self.__execute_command, args=(mr,))
thread.start()
while thread.is_alive():
self.connection.process_data_events()
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Result received:")
self.logger.info(self.result)
if (self.result is not True):
self.__send_error(json.dumps(mr))
return self.result
def __send_ack_nack(self, ack, channel, method):
channel.basic_ack(delivery_tag=method.delivery_tag)
if ack is True:
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Ack performed")
else:
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Ack performed BUT ERRORS OCCURRED!")
def __data_handler(self, channel, method, properties, body):
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + ' - New message received:')
error = False
try:
mr = json.loads(body.decode())
error = not self.__data_processing(channel, mr)
except json.JSONDecodeError as error:
self.logger.exception(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Malformed message", stack_info=False, exc_info=False)
self.logger.exception(error, stack_info=False, exc_info=False)
self.logger.exception(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - This message will be removed from the queue", stack_info=False, exc_info=False)
self.__send_ack_nack(False, channel, method)
time.sleep(60)
return
if self.result is True and error is False:
self.__send_ack_nack(True, channel, method)
else:
self.logger.error(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - An error has occured during processing")
if self.discarding_mode is True:
self.logger.error(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Discarding mode enabled.")
self.__send_ack_nack(True, channel, method)
else:
self.__send_ack_nack(False, channel, method)
time.sleep(5)
self.logger.info(self.rabbitmq_vhost + "/" + self.rabbitmq_queue + " - Data handling complete. Waiting for new messages")
def __send_error(self, message):
self.channel.basic_publish(exchange='', routing_key=self.rabbitmq_queue+'_errors', body=message)
if __name__ == '__main__':
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler = logging.handlers.TimedRotatingFileHandler("{}{}".format(os.environ['logging_file_path'], os.environ['logging_name']), when=os.environ['logging_file_rotate_when'], interval=int(os.environ['logging_file_rotate_interval']), backupCount=int(os.environ['logging_file_backup_count']))
handler.setFormatter(formatter)
logger = logging.getLogger(os.environ['logging_name'])
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
app = JixelGeneralPurposeRabbitConsumer(
os.environ['RABBITMQ_USERNAME'],
os.environ['RABBITMQ_PASSWORD'],
os.environ['rabbitmq_host'],
os.environ['rabbitmq_vhost'],
os.environ['rabbitmq_port'],
os.environ['rabbitmq_queue'],
os.environ['rabbitmq_heartbeat'],
os.environ['execution_path'],
os.environ['execution_command'],
logger,
os.environ['discarding_mode']
)
app.consume()

26
idrocap_wa/jixel-background-task-handler/jgprc.sh

@ -0,0 +1,26 @@
export rabbitmq_username='jixel'
export rabbitmq_password='jixel'
export rabbitmq_host='rabbitmq'
export rabbitmq_vhost='gp'
export rabbitmq_queue='gp'
export rabbitmq_port='5672'
export execution_path='/var/www/jixel/bin/'
export execution_command='gp'
export rabbitmq_heartbeat=60
export discarding_mode='False'
# Log file rotation policy:
# Current 'when' events supported:
# 'S' - Seconds
# 'M' - Minutes
# 'H' - Hours
# 'D' - Days
# 'midnight' - roll over at midnight
# 'W{0-6}' - roll over on a certain day; W0 - Monday, W1 - Tuesday, etc
export logging_file_rotate_when='H'
export logging_file_rotate_interval=24
export logging_name='JixelGeneralPurposeRabbitConsumer'
export logging_file_path='/general_purpose_rabbit_consumer/logs/'
export logging_file_backup_count=30
python3 ./jgprc.py

12
idrocap_wa/jixel-background-task-handler/jixel-background-task-handler.sh

@ -0,0 +1,12 @@
#!/bin/bash
if [ -f /run/secrets/rabbitmq_username ]; then
export RABBITMQ_USERNAME=$(cat /run/secrets/rabbitmq_username)
else
echo "Secret file 'rabbitmq_username' not found!" >&2
fi
if [ -f /run/secrets/rabbitmq_password ]; then
export RABBITMQ_PASSWORD=$(cat /run/secrets/rabbitmq_password)
else
echo "Secret file 'rabbitmq_password' not found!" >&2
fi
/usr/bin/python3 /usr/local/ies-solutions/jixel-background-task-handler/jbth.py

0
idrocap_wa/jixel-background-task-handler/jixel-cron.log

5
idrocap_wa/jixel-background-task-handler/jixel-cron.sh

@ -0,0 +1,5 @@
#!/bin/bash
crond
crontab /usr/local/ies-solutions/jixel-cron/jixel-cron.txt
tail -f /usr/local/ies-solutions/jixel-cron/jixel-cron.txt

6
idrocap_wa/jixel-background-task-handler/jixel-cron.txt

@ -0,0 +1,6 @@
# always put 3 rows: service called, date and the actual service called output:
* * * * * echo "/var/www/jixel/bin/cake" >> /usr/local/ies-solutions/jixel-cron/jixel-cron.log
* * * * * date >> /usr/local/ies-solutions/jixel-cron/jixel-cron.log
* * * * * /var/www/jixel/bin/cake >> /usr/local/ies-solutions/jixel-cron/jixel-cron.log
0 0 * * 0 rm -f /usr/local/ies-solutions/jixel-cron/jixel-cron.log
# keep this line empty!

12
idrocap_wa/jixel-background-task-handler/jixel-general-purpose-rabbit-consumer.sh

@ -0,0 +1,12 @@
#!/bin/bash
if [ -f /run/secrets/rabbitmq_username ]; then
export RABBITMQ_USERNAME=$(cat /run/secrets/rabbitmq_username)
else
echo "Secret file 'rabbitmq_username' not found!" >&2
fi
if [ -f /run/secrets/rabbitmq_password ]; then
export RABBITMQ_PASSWORD=$(cat /run/secrets/rabbitmq_password)
else
echo "Secret file 'rabbitmq_password' not found!" >&2
fi
/usr/bin/python3 /usr/local/ies-solutions/jixel-general-purpose-rabbit-consumer/jgprc.py

12
idrocap_wa/phpcs.xml

@ -0,0 +1,12 @@
<?xml version="1.0"?>
<ruleset name="App">
<config name="installed_paths" value="../../cakephp/cakephp-codesniffer"/>
<rule ref="CakePHP"/>
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint">
<exclude-pattern>*/src/Controller/*</exclude-pattern>
</rule>
<file>src/</file>
<file>tests/</file>
</ruleset>

6
idrocap_wa/phpstan.neon

@ -0,0 +1,6 @@
parameters:
level: 8
treatPhpDocTypesAsCertain: false
checkGenericClassInNonGenericObjectType: false
paths:
- src/

37
idrocap_wa/phpunit.xml.dist

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
colors="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="tests/bootstrap.php"
cacheDirectory=".phpunit.cache"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd">
<php>
<ini name="memory_limit" value="-1"/>
<ini name="apc.enable_cli" value="1"/>
</php>
<!-- Add any additional test suites you want to run here -->
<testsuites>
<testsuite name="app">
<directory>tests/TestCase/</directory>
</testsuite>
<!-- Add plugin test suites here. -->
</testsuites>
<!-- Load extension for fixtures -->
<extensions>
<bootstrap class="Cake\TestSuite\Fixture\Extension\PHPUnitExtension"/>
</extensions>
<!-- Ignore vendor tests in code coverage reports -->
<source>
<include>
<directory suffix=".php">src/</directory>
<directory suffix=".php">plugins/*/src/</directory>
</include>
<exclude>
<file>src/Console/Installer.php</file>
</exclude>
</source>
</phpunit>

0
idrocap_wa/plugins/.gitkeep

15
idrocap_wa/psalm.xml

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<psalm
errorLevel="2"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

0
idrocap_wa/resources/.gitkeep

279
idrocap_wa/resources/locales/cake.pot

@ -0,0 +1,279 @@
# LANGUAGE translation of CakePHP Application
# Copyright YEAR NAME <EMAIL@ADDRESS>
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: CakePHP 4.4.5\n"
"POT-Creation-Date: 2023-02-28 09:27+0000\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: ./templates/Error/error400.php:39
#: ./templates/Error/error500.php:43
msgid "Error"
msgstr ""
#: ./templates/Error/error400.php:40
msgid "The requested address {0} was not found on this server."
msgstr ""
#: ./templates/Error/error500.php:41
#: ./vendor/cakephp/cakephp/src/Error/Renderer/WebExceptionRenderer.php:361
msgid "An Internal Error Has Occurred."
msgstr ""
#: ./vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php:462
msgid "You are not authorized to access that location."
msgstr ""
#: ./vendor/cakephp/cakephp/src/Error/Renderer/WebExceptionRenderer.php:359
msgid "Not Found"
msgstr ""
#: ./vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:390
msgid "Missing or incorrect CSRF cookie type."
msgstr ""
#: ./vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:394
msgid "Missing or invalid CSRF cookie."
msgstr ""
#: ./vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:417
#: ./vendor/cakephp/cakephp/src/Http/Middleware/SessionCsrfProtectionMiddleware.php:265
msgid "CSRF token from either the request body or request headers did not match or is missing."
msgstr ""
#: ./vendor/cakephp/cakephp/src/Http/Middleware/SessionCsrfProtectionMiddleware.php:247
msgid "Missing or incorrect CSRF session key"
msgstr ""
#: ./vendor/cakephp/cakephp/src/Http/Response.php:1489
msgid "The requested file contains `..` and will not be read."
msgstr ""
#: ./vendor/cakephp/cakephp/src/Http/Response.php:1497
msgid "The requested file was not found"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:116
msgid "{0,number,#,###.##} KB"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:118
msgid "{0,number,#,###.##} MB"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:120
msgid "{0,number,#,###.##} GB"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:122
msgid "{0,number,#,###.##} TB"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:114
msgid "{0,number,integer} Byte"
msgid_plural "{0,number,integer} Bytes"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:86
msgid "{0} from now"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:86
msgid "{0} ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:89
msgid "{0} after"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:89
msgid "{0} before"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:120
msgid "just now"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:157
msgid "about a second ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:158
msgid "about a minute ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:159
msgid "about an hour ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:160
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:370
msgid "about a day ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:161
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:371
msgid "about a week ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:162
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:372
msgid "about a month ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:163
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:373
msgid "about a year ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:174
msgid "in about a second"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:175
msgid "in about a minute"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:176
msgid "in about an hour"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:177
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:384
msgid "in about a day"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:178
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:385
msgid "in about a week"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:179
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:386
msgid "in about a month"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:180
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:387
msgid "in about a year"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:342
msgid "today"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:409
msgid "%s ago"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:410
msgid "on %s"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:53
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:132
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:354
msgid "{0} year"
msgid_plural "{0} years"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:57
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:135
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:357
msgid "{0} month"
msgid_plural "{0} months"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:63
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:138
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:360
msgid "{0} week"
msgid_plural "{0} weeks"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:65
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:141
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:363
msgid "{0} day"
msgid_plural "{0} days"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:70
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:144
msgid "{0} hour"
msgid_plural "{0} hours"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:74
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:147
msgid "{0} minute"
msgid_plural "{0} minutes"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:78
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:150
msgid "{0} second"
msgid_plural "{0} seconds"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/ORM/RulesChecker.php:63
msgid "This value is already in use"
msgstr ""
#: ./vendor/cakephp/cakephp/src/ORM/RulesChecker.php:110
msgid "This value does not exist"
msgstr ""
#: ./vendor/cakephp/cakephp/src/ORM/RulesChecker.php:232
msgid "Cannot modify row: a constraint for the `{0}` association fails."
msgstr ""
#: ./vendor/cakephp/cakephp/src/ORM/RulesChecker.php:270
msgid "The count does not match {0}{1}"
msgstr ""
#: ./vendor/cakephp/cakephp/src/Utility/Text.php:923
msgid "and"
msgstr ""
#: ./vendor/cakephp/cakephp/src/Validation/Validator.php:2539
msgid "This field is required"
msgstr ""
#: ./vendor/cakephp/cakephp/src/Validation/Validator.php:2559
#: ./vendor/cakephp/cakephp/src/View/Form/ArrayContext.php:249
msgid "This field cannot be left empty"
msgstr ""
#: ./vendor/cakephp/cakephp/src/Validation/Validator.php:2711
msgid "The provided value is invalid"
msgstr ""
#: ./vendor/cakephp/cakephp/src/View/Helper/FormHelper.php:979
msgid "Edit {0}"
msgstr ""
#: ./vendor/cakephp/cakephp/src/View/Helper/FormHelper.php:981
msgid "New {0}"
msgstr ""
#: ./vendor/cakephp/cakephp/src/View/Helper/FormHelper.php:1917
msgid "Submit"
msgstr ""

1709
idrocap_wa/resources/locales/default.pot

File diff soppresso perché troppo grande Load Diff

1709
idrocap_wa/resources/locales/en/default.po

File diff soppresso perché troppo grande Load Diff

1709
idrocap_wa/resources/locales/en/previous_default.po

File diff soppresso perché troppo grande Load Diff

279
idrocap_wa/resources/locales/it/cake.po

@ -0,0 +1,279 @@
# LANGUAGE translation of CakePHP Application
# Copyright YEAR NAME <EMAIL@ADDRESS>
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: CakePHP 4.4.5\n"
"POT-Creation-Date: 2023-02-28 09:27+0000\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: ./templates/Error/error400.php:39
#: ./templates/Error/error500.php:43
msgid "Error"
msgstr "Errore"
#: ./templates/Error/error400.php:40
msgid "The requested address {0} was not found on this server."
msgstr "L'indirizzo richiesto {0} non è stato trovato su questo server."
#: ./templates/Error/error500.php:41
#: ./vendor/cakephp/cakephp/src/Error/Renderer/WebExceptionRenderer.php:361
msgid "An Internal Error Has Occurred."
msgstr "Errore interno."
#: ./vendor/cakephp/cakephp/src/Controller/Component/AuthComponent.php:462
msgid "You are not authorized to access that location."
msgstr "Non sei autorizzato ad accedere a quella risorsa."
#: ./vendor/cakephp/cakephp/src/Error/Renderer/WebExceptionRenderer.php:359
msgid "Not Found"
msgstr "Non Trovato"
#: ./vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:390
msgid "Missing or incorrect CSRF cookie type."
msgstr "tipo di CSRF cookie mancante o incorretto."
#: ./vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:394
msgid "Missing or invalid CSRF cookie."
msgstr "CSRF cookie mancante o incorretto."
#: ./vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:417
#: ./vendor/cakephp/cakephp/src/Http/Middleware/SessionCsrfProtectionMiddleware.php:265
msgid "CSRF token from either the request body or request headers did not match or is missing."
msgstr "Il CSRF token è mancante o quello presente nel body non corrisponde."
#: ./vendor/cakephp/cakephp/src/Http/Middleware/SessionCsrfProtectionMiddleware.php:247
msgid "Missing or incorrect CSRF session key"
msgstr "Chiave di sessione CSRF incorretta o mancante"
#: ./vendor/cakephp/cakephp/src/Http/Response.php:1489
msgid "The requested file contains `..` and will not be read."
msgstr "Il file richiesto contiene `..` e quindi non verrà letto."
#: ./vendor/cakephp/cakephp/src/Http/Response.php:1497
msgid "The requested file was not found"
msgstr "Il file richiesto non è stato trovato"
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:116
msgid "{0,number,#,###.##} KB"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:118
msgid "{0,number,#,###.##} MB"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:120
msgid "{0,number,#,###.##} GB"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:122
msgid "{0,number,#,###.##} TB"
msgstr ""
#: ./vendor/cakephp/cakephp/src/I18n/Number.php:114
msgid "{0,number,integer} Byte"
msgid_plural "{0,number,integer} Bytes"
msgstr[0] ""
msgstr[1] ""
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:86
msgid "{0} from now"
msgstr "{0} da adesso"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:86
msgid "{0} ago"
msgstr "{0} fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:89
msgid "{0} after"
msgstr "{0} dopo"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:89
msgid "{0} before"
msgstr "{0} prima"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:120
msgid "just now"
msgstr "in questo momento"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:157
msgid "about a second ago"
msgstr "circa un secondo fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:158
msgid "about a minute ago"
msgstr "circa un minuto fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:159
msgid "about an hour ago"
msgstr "circa un'ora fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:160
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:370
msgid "about a day ago"
msgstr "circa un giorno fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:161
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:371
msgid "about a week ago"
msgstr "circa una settimana fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:162
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:372
msgid "about a month ago"
msgstr "circa un mese fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:163
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:373
msgid "about a year ago"
msgstr "circa un anno fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:174
msgid "in about a second"
msgstr "fra circa un secondo"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:175
msgid "in about a minute"
msgstr "fra circa un minuto"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:176
msgid "in about an hour"
msgstr "fra circa un'ora"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:177
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:384
msgid "in about a day"
msgstr "fra circa un giorno"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:178
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:385
msgid "in about a week"
msgstr "tra circa una settimana"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:179
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:386
msgid "in about a month"
msgstr "tra circa un mese"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:180
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:387
msgid "in about a year"
msgstr "tra circa un anno"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:342
msgid "today"
msgstr "oggi"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:409
msgid "%s ago"
msgstr "%s fa"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:410
msgid "on %s"
msgstr "%s"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:53
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:132
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:354
msgid "{0} year"
msgid_plural "{0} years"
msgstr[0] "{0} anno"
msgstr[1] "{0} anni"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:57
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:135
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:357
msgid "{0} month"
msgid_plural "{0} months"
msgstr[0] "{0} mese"
msgstr[1] "{0} mesi"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:63
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:138
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:360
msgid "{0} week"
msgid_plural "{0} weeks"
msgstr[0] "{0} settimana"
msgstr[1] "{0} settimane"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:65
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:141
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:363
msgid "{0} day"
msgid_plural "{0} days"
msgstr[0] "{0} giorno"
msgstr[1] "{0} giorni"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:70
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:144
msgid "{0} hour"
msgid_plural "{0} hours"
msgstr[0] "{0} ora"
msgstr[1] "{0} ore"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:74
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:147
msgid "{0} minute"
msgid_plural "{0} minutes"
msgstr[0] "{0} minuto"
msgstr[1] "{0} minuti"
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:78
#: ./vendor/cakephp/cakephp/src/I18n/RelativeTimeFormatter.php:150
msgid "{0} second"
msgid_plural "{0} seconds"
msgstr[0] "{0} secondo"
msgstr[1] "{0} secondi"
#: ./vendor/cakephp/cakephp/src/ORM/RulesChecker.php:63
msgid "This value is already in use"
msgstr "Questo valore è già in uso"
#: ./vendor/cakephp/cakephp/src/ORM/RulesChecker.php:110
msgid "This value does not exist"
msgstr "Questo valore non esiste"
#: ./vendor/cakephp/cakephp/src/ORM/RulesChecker.php:232
msgid "Cannot modify row: a constraint for the `{0}` association fails."
msgstr "Impossibile modificare: il vincolo per l'associazione `{0}` fallisce."
#: ./vendor/cakephp/cakephp/src/ORM/RulesChecker.php:270
msgid "The count does not match {0}{1}"
msgstr "Il conteggio non corrisponde {0}{1}"
#: ./vendor/cakephp/cakephp/src/Utility/Text.php:923
msgid "and"
msgstr "e"
#: ./vendor/cakephp/cakephp/src/Validation/Validator.php:2539
msgid "This field is required"
msgstr "Questo campo è richiesto"
#: ./vendor/cakephp/cakephp/src/Validation/Validator.php:2559
#: ./vendor/cakephp/cakephp/src/View/Form/ArrayContext.php:249
msgid "This field cannot be left empty"
msgstr "Questo campo non può essere lasciato vuoto"
#: ./vendor/cakephp/cakephp/src/Validation/Validator.php:2711
msgid "The provided value is invalid"
msgstr "Il valore fornito non è valido"
#: ./vendor/cakephp/cakephp/src/View/Helper/FormHelper.php:979
msgid "Edit {0}"
msgstr "Modifica {0}"
#: ./vendor/cakephp/cakephp/src/View/Helper/FormHelper.php:981
msgid "New {0}"
msgstr "Nuovo {0}"
#: ./vendor/cakephp/cakephp/src/View/Helper/FormHelper.php:1917
msgid "Submit"
msgstr "Salva"

268
idrocap_wa/src/Application.php

@ -0,0 +1,268 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App;
use Cake\Core\Configure;
use Cake\Core\ContainerInterface;
use Cake\Datasource\FactoryLocator;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\Middleware\BodyParserMiddleware;
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Http\MiddlewareQueue;
use Cake\ORM\Locator\TableLocator;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Identifier\AbstractIdentifier;
use Authentication\Identifier\IdentifierInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Cake\Http\Middleware\EncryptedCookieMiddleware;
use Cake\Routing\Router;
use Psr\Http\Message\ServerRequestInterface;
use Cake\I18n\Middleware\LocaleSelectorMiddleware;
use App\Middleware\UserTimezoneDatetimeRequestDataMiddleware;
/**
* Application setup class.
*
* This defines the bootstrapping logic and middleware layers you
* want to use in your application.
*/
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{
/**
* Load all the application configuration and bootstrap logic.
*
* @return void
*/
public function bootstrap(): void
{
// Call parent to load bootstrap from files.
parent::bootstrap();
$this->addPlugin('Authentication');
if (PHP_SAPI === 'cli') {
$this->bootstrapCli();
} else {
FactoryLocator::add(
'Table',
(new TableLocator())->allowFallbackClass(false)
);
}
/*
* Only try to load DebugKit in development mode
* Debug Kit should not be installed on a production system
*/
if (Configure::read('debug')) {
Configure::write('DebugKit.safeTld', ['it']);
}
// Load more plugins here
$this->addPlugin('Muffin/Trash');
$this->addPlugin('CsvView');
$this->addPlugin('CakeSentry');
}
/**
* Returns a service provider instance.
*
* @param \Psr\Http\Message\ServerRequestInterface $request Request
* @return \Authentication\AuthenticationServiceInterface
*/
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$service = new AuthenticationService();
$fields = [
AbstractIdentifier::CREDENTIAL_USERNAME => 'username',
AbstractIdentifier::CREDENTIAL_PASSWORD => 'password'
];
if (strpos($request->getPath(), '/api/') !== 0) {
// NORMAL REQUESTS
// se si tratta della request "/notifications/index" non eseguire il redirect e restituisci 401!
if ($request->getPath() !== '/notifications/index') {
// Define where users should be redirected to when they are not authenticated
$service->setConfig([
'unauthenticatedRedirect' => Router::url([
'prefix' => false,
'plugin' => null,
'controller' => 'Users',
'action' => 'login',
]),
'queryParam' => 'redirect',
]);
}
// Load the authenticators. Session should be first.
$service->loadAuthenticator('Authentication.Session');
$service->loadAuthenticator('Authentication.Cookie', [
'fields' => $fields,
'loginUrl' => Router::url([
'prefix' => false,
'plugin' => null,
'controller' => 'Users',
'action' => 'login',
]),
]);
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => Router::url([
'prefix' => false,
'plugin' => null,
'controller' => 'Users',
'action' => 'login',
]),
]);
// Load identifiers
$service->loadIdentifier('JixelPassword', [
'fields' => $fields,
]);
} else {
// API REQUESTS
if (strpos($request->getPath(), '/api/users/login') === 0) {
// API REQUEST LOGIN
// Load the authenticators.
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => Router::url([
'prefix' => 'Api',
'plugin' => null,
'controller' => 'Users',
'action' => 'login',
]),
]);
// Load identifiers
$service->loadIdentifier('Authentication.Password', compact('fields'));
} else {
// Load identifiers
$service->loadIdentifier('Authentication.JwtSubject', [
'tokenField' => 'username',
]);
// Load the authenticators
$service->loadAuthenticator('Authentication.Jwt', [
'secretKey' => file_get_contents(Configure::read('App.jwt.keys.public')),
'algorithm' => Configure::read('App.jwt.algorithm'),
'returnPayload' => false
]);
}
}
return $service;
}
/**
* Setup the middleware queue your application will use.
*
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* @return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue
->add(new LocaleSelectorMiddleware(['it', 'en']))
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(new ErrorHandlerMiddleware(Configure::read('Error')))
// Handle plugin/theme assets like CakePHP normally does.
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime'),
]))
// Add routing middleware.
// If you have a large number of routes connected, turning on routes
// caching in production could improve performance. For that when
// creating the middleware instance specify the cache config name by
// using it's second constructor argument:
// `new RoutingMiddleware($this, '_cake_routes_')`
->add(new RoutingMiddleware($this))
// Parse various types of encoded request bodies so that they are
// available as array through $request->getData()
// https://book.cakephp.org/4/en/controllers/middleware.html#body-parser-middleware
->add(new BodyParserMiddleware());
// Cross Site Request Forgery (CSRF) Protection Middleware
// https://book.cakephp.org/4/en/security/csrf.html#cross-site-request-forgery-csrf-middleware
$csrf = new CsrfProtectionMiddleware();
// Token check will be skipped when callback returns `true`.
$csrf->skipCheckCallback(function ($request) {
// Skip token check for API URLs.
if ($request->getParam('prefix') === 'Api') {
return true;
}
// Skip token check for ajax calls.
if ($request->is('ajax')) {
return true;
}
});
// Ensure routing middleware is added to the queue before CSRF protection middleware.
$middlewareQueue->add($csrf)
// encryption/decryption management of listed cookies:
->add(new EncryptedCookieMiddleware(['CookieAuth'], Configure::read('Security.CookieEncryptionKey')))
// Add the AuthenticationMiddleware. It should be
// after routing and body parser.
->add(new AuthenticationMiddleware($this))
->add(new UserTimezoneDatetimeRequestDataMiddleware());
return $middlewareQueue;
}
/**
* Register application container services.
*
* @param \Cake\Core\ContainerInterface $container The Container to update.
* @return void
* @link https://book.cakephp.org/4/en/development/dependency-injection.html#dependency-injection
*/
public function services(ContainerInterface $container): void
{
}
/**
* Bootstrapping for CLI application.
*
* That is when running commands.
*
* @return void
*/
protected function bootstrapCli(): void
{
$this->addOptionalPlugin('Cake/Repl');
// Load more plugins here
}
}

95
idrocap_wa/src/Command/AddCadastralCodeToUsesCommand.php

@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
use App\WGS\Geo\CadastralUtils;
class AddCadastralCodeToUsesCommand extends Command
{
/**
* Hook method for defining this command's option parser.
*
* @see https://book.cakephp.org/4/en/console-commands/commands.html#defining-arguments-and-options
* @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
* @return \Cake\Console\ConsoleOptionParser The built parser.
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser = parent::buildOptionParser($parser);
$parser
->addArgument('chunk_size', [
'help' => 'the amount of records to process in chunk (at least 10 which is the default value)',
'required' => false,
]);
return $parser;
}
/*
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return null|void|int The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io)
{
$chunkSize = (int)($args->getArgument('chunk_size') ?? 10);
if ($chunkSize < 10) $io->abort("Please specify a chunk size of at least 10!");
$page = 1;
$total = 0;
$io->info("AddCadastralCodeToUsesCommand started!");
$io->info("Chunk size is: $chunkSize");
do {
$intendedUses = $this->fetchTable('WaterDrawingIntendedUses')->find()
->where([
// recuperiamo solo le entries di tipo uso IRRIGUO che non hanno il codice BELFIORE:
"WaterDrawingIntendedUses.water_drawing_intended_use_type_id" => 1,
"WaterDrawingIntendedUses.cadastral_code IS NULL",
// devono però avere foglio e particella validi:
"WaterDrawingIntendedUses.cadastral_sheet IS NOT NULL",
"WaterDrawingIntendedUses.cadastral_parcel IS NOT NULL",
"WaterDrawingIntendedUses.cadastral_sheet <> ''",
"WaterDrawingIntendedUses.cadastral_parcel <> ''",
])
->limit($chunkSize)
->page($page)
->all();
foreach($intendedUses as $intendedUse){
$io->info("Processing WaterDrawingIntendedUse with ID: $intendedUse->id");
// recuperiamo il codice BELFIORE dal primo delle derivazioni connesse alla pratica (se ve ne sono....):
$derivation = $this->fetchTable('WaterDrawingDerivations')->find()->where([
'WaterDrawingDerivations.water_drawing_paperwork_id' => $intendedUse->water_drawing_paperwork_id
])->first();
if (!isset($derivation->cadastral_code)) {
$io->warning('WARNING: related water_drawing_paperwork has no suitable water_drawing_derivations to get cadastral_code from -> SKIPPED!');
continue;
}
$intendedUse->cadastral_code = $derivation->cadastral_code;
$intendedUse->vegetation_match_status = CadastralUtils::isParcelWithinVegetationArea(strtolower($intendedUse->cadastral_code), $intendedUse->cadastral_sheet, $intendedUse->cadastral_parcel) ? 1 : -1;
if(!$this->fetchTable('WaterDrawingIntendedUses')->save($intendedUse, ['skipResetIsMismatch' => true])){
$io->warning('WARNING: Error during save!');
$io->warning(json_encode($intendedUse->getErrors()));
}
}
$count = $intendedUses->count();
$total += $count;
if ($count) $io->success("processed $count items for page $page");
$page++;
} while ($count === $chunkSize);
$io->success("total processed $total items!");
return static::CODE_SUCCESS;
}
}
?>

44
idrocap_wa/src/Command/AddDerivationCoordinatesCommand.php

@ -0,0 +1,44 @@
<?php
namespace App\Command;
use App\WGS\Geo\CadastralUtils;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
class AddDerivationCoordinatesCommand extends Command
{
/*
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return null|void|int The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io)
{
$derivations = $this->fetchTable('WaterDrawingDerivations')->find()->where([
'WaterDrawingDerivations.latitude' => '',
'WaterDrawingDerivations.longitude' => '',
'WaterDrawingDerivations.cadastral_code IS NOT NULL',
'WaterDrawingDerivations.cadastral_sheet IS NOT NULL',
'WaterDrawingDerivations.cadastral_parcel IS NOT NULL',
])->toArray();
foreach($derivations as $derivation){
$coordinates = CadastralUtils::getParcelCentroidCoordinates(strtolower($derivation->cadastral_code), (string) $derivation->cadastral_sheet, (string) $derivation->cadastral_parcel);
if(isset($coordinates)){
$derivation = $this->fetchTable('WaterDrawingDerivations')->patchEntity($derivation, [
'latitude' => $coordinates->latitude,
'longitude' => $coordinates->longitude,
'feature_collection' => '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":['. $coordinates->longitude .','. $coordinates->latitude .']},"properties":null}]}'
]);
if(!$this->fetchTable('WaterDrawingDerivations')->save($derivation)){
$io->out(__('Errore durante il salvataggio.'));
$io->out(json_encode($derivation->getErrors()));
}
}
}
return static::CODE_SUCCESS;
}
}
?>

57
idrocap_wa/src/Command/CheckExpiredWaterDrawingAntimafiaCertificateRequestsCommand.php

@ -0,0 +1,57 @@
<?php
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use App\WGS\NotificationsHandler\NotificationsHandler;
class CheckExpiredWaterDrawingAntimafiaCertificateRequestsCommand extends Command
{
/*
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return null|void|int The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io): int
{
$io->info("\n\nCheckExpiredWaterDrawingAntimafiaCertificateRequestsCommand: START!!!");
// prima recupero, per ogni pratica, le water_drawing_antimafia_certification_requests con ID massimo
// questo mi assicura di prendere in considerazione l'ultima richiesta fatta:
$latest_water_drawing_antimafia_certification_request_ids = $this->fetchTable('WaterDrawingAntimafiaCertificationRequests')
->find()
->select(['max_id' => 'MAX(WaterDrawingAntimafiaCertificationRequests.id)'])
->group('water_drawing_paperwork_id');
// quindi recuoero le pratiche per cui risulti l'ultima richiesta AM scaduta (termine dei 30gg)
// che sia in "stato richiesta" in modo da poter notificare i relativi utenti DRAR:
$to_be_notified_water_drawing_paperworks = $this->fetchTable('WaterDrawingPaperworks')
->find()
->matching('WaterDrawingAntimafiaCertificationRequests')
->where([
'WaterDrawingAntimafiaCertificationRequests.id IN' => $latest_water_drawing_antimafia_certification_request_ids,
'WaterDrawingAntimafiaCertificationRequests.water_drawing_antimafia_certification_request_status_id' => 2,
'WaterDrawingAntimafiaCertificationRequests.created <= NOW() - INTERVAL 30 DAY'
])
->toArray();
foreach($to_be_notified_water_drawing_paperworks as $waterDrawingPaperwork) {
// DISPATCH NOTIFICHE:
$io->info("\nCheckExpiredWaterDrawingAntimafiaCertificateRequestsCommand: richiesta AM scaduta per pratica con ID: " . $waterDrawingPaperwork->id);
if (isset($waterDrawingPaperwork->drar_user_id)) {
$io->success("CheckExpiredWaterDrawingAntimafiaCertificateRequestsCommand: Invio notifica a utente DRAR con ID: " . $waterDrawingPaperwork->drar_user_id);
// invio notifica a utente DRAR che aveva richiesto la certificazione antimafia perchè la richiesta è scaduta:
NotificationsHandler::dispatch($waterDrawingPaperwork->controllable_object_id, 'send_to_drar_user_antimafia_request_expired', ['drar_user_id' => $waterDrawingPaperwork->drar_user_id, 'link' => '/water_drawing_paperworks/view/' . $waterDrawingPaperwork->id, 'water_drawing_paperwork_id' => $waterDrawingPaperwork->id]);
} else {
$io->error("CheckExpiredWaterDrawingAntimafiaCertificateRequestsCommand: LA PRATICA NON HA UTENTI DRAR ASSEGNATI !!!!!");
}
}
$io->info("\n\nCheckExpiredWaterDrawingAntimafiaCertificateRequestsCommand: END!!!");
return static::CODE_SUCCESS;
}
}
?>

144
idrocap_wa/src/Command/CopyTranslationsCommand.php

@ -0,0 +1,144 @@
<?php
declare(strict_types=1);
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
/**
* CopyTranslations command.
*/
class CopyTranslationsCommand extends Command
{
private $pot_identifiers = [];
/**
* Hook method for defining this command's option parser.
*
* @see https://book.cakephp.org/4/en/console-commands/commands.html#defining-arguments-and-options
* @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
* @return \Cake\Console\ConsoleOptionParser The built parser.
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser = parent::buildOptionParser($parser);
$parser
->addArgument('template_filename', [
'help' => '.POT Template file (with full or relative path)',
'required' => true,
])
->addArgument('input_filename', [
'help' => '.PO Input file (with full or relative path)',
'required' => true,
])
->addArgument('output_filename', [
'help' => 'Output PO file (with full or relative path)',
'required' => true,
]);
return $parser;
}
/**
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return null|void|int The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io)
{
$template_filename = $args->getArgument('template_filename');
$input_filename = $args->getArgument('input_filename');
$output_filename = $args->getArgument('output_filename');
$io->info("\n\ntemplate file path: $template_filename, input file path: $input_filename, output file path: $output_filename\n\n");
if (!file_exists($template_filename)) $io->abort("\n\n\nError: File \"$template_filename\" doesn't exists!");
if (!file_exists($input_filename)) $io->abort("\n\n\nError: File \"$input_filename\" doesn't exists!");
// lettura pot e retrieving identifiers:
$template_filename_handle = fopen($template_filename, "r");
if ($template_filename_handle) {
$pot_row_count = 0;
$io->info("\n\n\nFetching translation identifiers from template file \"$template_filename\":\n");
while (($buffer = fgets($template_filename_handle, 4096)) !== false) {
if (substr($buffer, 0, 5) !== "msgid") continue;
$text = explode("\"\n", explode("msgid \"", $buffer)[1])[0];
if ($text === "") continue;
++$pot_row_count;
$io->info("msgid at row $pot_row_count: $text");
$this->pot_identifiers[$text] = "";
}
if (!feof($template_filename_handle)) {
$io->abort("\n\n\nError: unexpected fgets() fail");
}
fclose($template_filename_handle);
$io->success("\n\nNumber of identifiers successfully fetched from template file \"$template_filename\": $pot_row_count\n\n");
} else {
$io->abort("\n\n\nError: Unable to Open File \"$template_filename\"!");
}
// lettura input file e retrieving translations (x gli identifiers che matchano):
$input_filename_handle = fopen($input_filename, "r");
if ($input_filename_handle) {
$count = 0;
$last_identifier = null;
$io->info("\n\n\nRetrieving translations from input file \"$input_filename\":\n");
while (($buffer = fgets($input_filename_handle, 4096)) !== false) {
if (substr($buffer, 0, 6) === "msgid ") {
$last_identifier = explode("\"\n", explode("msgid \"", $buffer)[1])[0];
if ($last_identifier === "" || !isset($this->pot_identifiers[$last_identifier])) $last_identifier = null;
}
if (substr($buffer, 0, 6) === "msgstr" && isset($last_identifier)) {
$this->pot_identifiers[$last_identifier] = explode("\"\n", explode("msgstr \"", $buffer)[1])[0];
if (!empty($this->pot_identifiers[$last_identifier])) ++$count;
$io->info("Retrieved translations so far: $count - Parsing: \"$last_identifier\" = \"" . $this->pot_identifiers[$last_identifier] . "\"");
}
}
if (!feof($input_filename_handle)) {
$io->abort("\n\n\nError: unexpected fgets() fail");
}
fclose($input_filename_handle);
$io->success("\n\nSuccessfully retrieved $count translations from input po file \"$input_filename\"!\n\n");
} else {
$io->abort("\n\n\nError: Unable to Open File \"$input_filename\"!");
}
// creazione file .PO di output partendo da struttura del .POT fornito e arricchito con le traduzioni recuperate dal file di input
$template_filename_handle = fopen($template_filename, "r");
$output_filename_handle = fopen($output_filename, 'w');
if ($template_filename_handle) {
$count = 0;
$last_identifier = null;
$io->info("\n\n\nCreating new translation file \"$output_filename\":\n");
while (($buffer = fgets($template_filename_handle, 4096)) !== false) {
if (substr($buffer, 0, 5) === "msgid") {
$last_identifier = explode("\"\n", explode("msgid \"", $buffer)[1])[0];
if ($last_identifier !== "" && !empty($this->pot_identifiers[$last_identifier])) {
++$count;
} else {
$last_identifier = null;
}
}
if (substr($buffer, 0, 6) === "msgstr" && isset($last_identifier)) {
$io->info("Adding translation number: $count for identifier: \"$last_identifier\"" . " with translation: \"" . $this->pot_identifiers[$last_identifier] . "\"");
$buffer = "msgstr \"" . $this->pot_identifiers[$last_identifier] . "\"\n";
}
fwrite($output_filename_handle, $buffer);
}
if (!feof($template_filename_handle)) {
$io->abort("\n\n\nError: unexpected fgets() fail");
}
fclose($template_filename_handle);
fclose($output_filename_handle);
$io->success("\n\nSuccessfully created po file \"$output_filename\" with $count added translations!\n\n");
} else {
$io->abort("\n\n\nError: Unable to Open File \"$template_filename\"!");
}
}
}

107
idrocap_wa/src/Command/IntendedUseVegetationCheckCommand.php

@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
use App\WGS\Geo\CadastralUtils;
use App\WGS\NotificationsHandler\NotificationsHandler;
class IntendedUseVegetationCheckCommand extends Command
{
/**
* Hook method for defining this command's option parser.
*
* @see https://book.cakephp.org/4/en/console-commands/commands.html#defining-arguments-and-options
* @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
* @return \Cake\Console\ConsoleOptionParser The built parser.
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser = parent::buildOptionParser($parser);
$parser->addArgument('data', [
'help' => 'the serialized data to process',
'required' => true,
]);
return $parser;
}
/*
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return null|void|int The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io)
{
$io->info("IntendedUseVegetationCheckCommand - JOB STARTED!");
$data = $args->getArgument('data');
$io->info("IntendedUseVegetationCheckCommand - received data: {$data}");
$deserialized_data = json_decode($data, true);
if (empty($deserialized_data['water_drawing_paperwork_id'])) {
$io->abort("IntendedUseVegetationCheckCommand - water_drawing_paperwork_id is missing!");
}
$water_drawing_paperwork = $this->fetchTable('WaterDrawingPaperworks')->find()->where(['WaterDrawingPaperworks.id' => $deserialized_data['water_drawing_paperwork_id']])->first();
if (!$water_drawing_paperwork) {
$io->abort("IntendedUseVegetationCheckCommand - water_drawing_paperwork with ID: {$deserialized_data['water_drawing_paperwork_id']} not found!");
}
$water_drawing_intended_uses = $this->fetchTable('WaterDrawingIntendedUses')->find()->where(['WaterDrawingIntendedUses.water_drawing_paperwork_id' => $water_drawing_paperwork->id])->all();
$intended_use_linked = count($water_drawing_intended_uses);
$intended_use_processed = 0;
foreach ($water_drawing_intended_uses as $water_drawing_intended_use) {
$io->info("IntendedUseVegetationCheckCommand - checking linked water_drawing_intended_use with ID: {$water_drawing_intended_use->id}, cadastral_code: {$water_drawing_intended_use->cadastral_code}, cadastral_sheet: {$water_drawing_intended_use->cadastral_sheet}, cadastral_parcel: {$water_drawing_intended_use->cadastral_parcel}, water_drawing_intended_use_type_id: {$water_drawing_intended_use->water_drawing_intended_use_type_id}...");
if ($water_drawing_intended_use->water_drawing_intended_use_type_id != 1) {
$io->comment("IntendedUseVegetationCheckCommand - linked water_drawing_intended_use with ID: {$water_drawing_intended_use->id} has water_drawing_intended_use_type_id: {$water_drawing_intended_use->water_drawing_intended_use_type_id} (must be 1), SKIPPED!");
continue;
}
if (empty($water_drawing_intended_use->cadastral_code)) {
$io->comment("IntendedUseVegetationCheckCommand - linked water_drawing_intended_use with ID: {$water_drawing_intended_use->id} has NO valid cadastral_code, SKIPPED!");
continue;
}
if (empty($water_drawing_intended_use->cadastral_sheet)) {
$io->comment("IntendedUseVegetationCheckCommand - linked water_drawing_intended_use with ID: {$water_drawing_intended_use->id} has NO valid cadastral_sheet, SKIPPED!");
continue;
}
if (empty($water_drawing_intended_use->cadastral_parcel)) {
$io->comment("IntendedUseVegetationCheckCommand - linked water_drawing_intended_use with ID: {$water_drawing_intended_use->id} has NO valid cadastral_parcel, SKIPPED!");
continue;
}
$water_drawing_intended_use->vegetation_match_status = CadastralUtils::isParcelWithinVegetationArea(strtolower($water_drawing_intended_use->cadastral_code), $water_drawing_intended_use->cadastral_sheet, $water_drawing_intended_use->cadastral_parcel) ? 1 : -1;
if ($water_drawing_intended_use->vegetation_match_status == -1) {
$io->comment("IntendedUseVegetationCheckCommand - linked water_drawing_intended_use with ID: {$water_drawing_intended_use->id} has NO MATCHING INTENDED USE!!!");
} else {
$io->success("IntendedUseVegetationCheckCommand - linked water_drawing_intended_use with ID: {$water_drawing_intended_use->id} has PERFECT MATCHING INTENDED USE!");
}
if (!$this->fetchTable('WaterDrawingIntendedUses')->save($water_drawing_intended_use, ['skipResetIsMismatch' => true])) {
$io->error("IntendedUseVegetationCheckCommand - error saving processed water_drawing_intended_use with ID: {$water_drawing_intended_use->id}");
} else {
$intended_use_processed++;
}
}
$io->success("IntendedUseVegetationCheckCommand - checked {$intended_use_linked} linked intended uses and successfully processed {$intended_use_processed} of them!");
if (!empty($deserialized_data['notification_user_id'])) {
$user_to_be_notified_id = $deserialized_data['notification_user_id'];
$io->info("IntendedUseVegetationCheckCommand - sending notification to user with ID: {$user_to_be_notified_id}");
NotificationsHandler::dispatch($water_drawing_paperwork->controllable_object_id, 'send_to_last_edit_user_intended_use_check_completed', ['last_edit_user_id' => $user_to_be_notified_id, 'link' => '/water_drawing_paperworks/view' . ($water_drawing_paperwork->scanned ? '_scan' : '') .'/' . $water_drawing_paperwork->id, 'water_drawing_paperwork_id' => $water_drawing_paperwork->id]);
} else {
$io->comment("IntendedUseVegetationCheckCommand - notification_user_id is missing, skipping notification!");
}
$io->info("IntendedUseVegetationCheckCommand - JOB ENDED!");
return static::CODE_SUCCESS;
}
}
?>

65
idrocap_wa/src/Command/NormalizeApplicantProvinceCommand.php

@ -0,0 +1,65 @@
<?php
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
class NormalizeApplicantProvinceCommand extends Command
{
/*
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return null|void|int The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io): int
{
$applicants = $this->fetchTable('Applicants')->find()->where(['Applicants.province IN' => ['Palermo', 'Catania', 'Caltanissetta', 'Enna', 'Ragusa', 'Agrigento', 'Messina', 'Siracusa', 'Trapani']])->toArray();
foreach($applicants as $applicant){
switch(strtolower($applicant->province)){
case 'catania':
$province = 'CT';
break;
case 'palermo':
$province = 'PA';
break;
case 'caltanissetta':
$province = 'CL';
break;
case 'enna':
$province = 'EN';
break;
case 'ragusa':
$province = 'RG';
break;
case 'siracusa':
$province = 'SR';
break;
case 'trapani':
$province = 'TP';
break;
case 'messina':
$province = 'ME';
break;
case 'agrigento':
$province = 'AG';
break;
}
if(!isset($province)) continue;
$applicant = $this->fetchTable('Applicants')->patchEntity($applicant, [
'province' => $province
]);
if(!$this->fetchTable('Applicants')->save($applicant, [
'checkRules' => false
])){
$io->out(__('Errore durante il salvataggio'));
$io->out(json_encode($applicant->getErrors(), JSON_PRETTY_PRINT));
}
$province = null;
}
return static::CODE_SUCCESS;
}
}
?>

65
idrocap_wa/src/Command/NotificationsHandlerCommand.php

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
use App\WGS\NotificationsHandler\NotificationsHandler;
/**
* NotificationsHandler command.
*/
class NotificationsHandlerCommand extends Command
{
/**
* Hook method for defining this command's option parser.
*
* @see https://book.cakephp.org/4/en/console-commands/commands.html#defining-arguments-and-options
* @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
* @return \Cake\Console\ConsoleOptionParser The built parser.
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser = parent::buildOptionParser($parser);
$parser->addArgument('data', [
'help' => 'the serialized data to process',
'required' => true,
]);
return $parser;
}
/**
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return null|void|int The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io): int
{
$data = $args->getArgument('data');
$io->info("NotificationsHandlerCommand - received data: {$data}");
$deserialized_data = json_decode($data, true);
if (empty($deserialized_data['background_task_id'])) {
$io->abort("NotificationsHandlerCommand - background_task_id is missing!");
}
$background_task = $this->fetchTable('BackgroundTasks')->find()->where(['BackgroundTasks.id' => $deserialized_data['background_task_id']])->first();
if (!$background_task) {
$io->abort("NotificationsHandlerCommand - background_task not found!");
}
$request = json_decode($background_task->data, true);
NotificationsHandler::executeDispatch($request['co_id'], $request['notification_code'], $request['options'], $request['group'], $request['user_logged']);
$io->success("NotificationsHandlerCommand - successfully processed!");
return static::CODE_SUCCESS;
}
}

98
idrocap_wa/src/Command/UnpackWaterDrawingPaperworkSnapshotsCommand.php

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
use App\WGS\SnapshotsHandler\SnapshotsHandler;
use Cake\Datasource\ConnectionManager;
/**
* UnpackWaterDrawingPaperworkSnapshots command.
*/
class UnpackWaterDrawingPaperworkSnapshotsCommand extends Command
{
/**
* Hook method for defining this command's option parser.
*
* @see https://book.cakephp.org/4/en/console-commands/commands.html#defining-arguments-and-options
* @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
* @return \Cake\Console\ConsoleOptionParser The built parser.
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser = parent::buildOptionParser($parser);
$parser->setDescription('Unpacks water_drawing_paperworks from Snapshots and store them to unpacked_water_drawing_paperwork_snapshots table with the last unpacking version number available');
$parser
->addOptions([
'limit' => [
'short' => 'l',
'help' => 'Number of rows to process',
'boolean' => false,
'required' => true,
],
]);
return $parser;
}
/**
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return null|void|int The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io)
{
$limit = $args->getOption('limit');
if ($limit < 1) $io->abort("You must specify an Int number greater than ZERO!");
$io->info("trying processing $limit items...");
$lastWaterDrawingPaperworkUnpackingVersion = SnapshotsHandler::getLastWaterDrawingPaperworkUnpackingVersion();
$io->info("lastWaterDrawingPaperworkUnpackingVersion: $lastWaterDrawingPaperworkUnpackingVersion");
$processed = 0;
$unpackable_water_drawing_paperwork_snapshots = $this->fetchTable('Snapshots')->find()->where(['Snapshots.controllable_object_type_id' => 9, 'OR' => ['Snapshots.unpacked_with_version is NULL', 'Snapshots.unpacked_with_version <>' => $lastWaterDrawingPaperworkUnpackingVersion]])->limit((int)$limit)->toArray();
$io->info("found " . count($unpackable_water_drawing_paperwork_snapshots) . " unpackable items.");
if (count($unpackable_water_drawing_paperwork_snapshots) > 0) ConnectionManager::get('default')->begin();
$this->fetchTable('WaterDrawingPaperworks')->removeBehavior('Trash');
foreach ($unpackable_water_drawing_paperwork_snapshots as $unpackable_water_drawing_paperwork_snapshot) {
$io->info("\nUnpackedWaterDrawingPaperworkSnapshot entity:");
$unpackedWaterDrawingPaperworkSnapshot = SnapshotsHandler::getUnpackedWaterDrawingPaperworkSnapshot($unpackable_water_drawing_paperwork_snapshot->id);
if (!$unpackedWaterDrawingPaperworkSnapshot) {
ConnectionManager::get('default')->rollback();
$io->abort("\nError retrieving unpackedWaterDrawingPaperworkSnapshot!");
}
$io->info(json_encode($unpackedWaterDrawingPaperworkSnapshot));
$io->info("\nSaving unpacked water_drawing_paperwork snapshot to unpackedWaterDrawingPaperworkSnapshots table...");
if (!$this->fetchTable('UnpackedWaterDrawingPaperworkSnapshots')->save($unpackedWaterDrawingPaperworkSnapshot)) {
ConnectionManager::get('default')->rollback();
$io->abort("\nError saving unpackedWaterDrawingPaperworkSnapshot: " . json_encode($unpackedWaterDrawingPaperworkSnapshot->getErrors()));
}
$io->info("\nMarking and saving snapshot with ID " . $unpackable_water_drawing_paperwork_snapshot->id . " as unpacked with version: " . $lastWaterDrawingPaperworkUnpackingVersion);
$unpackable_water_drawing_paperwork_snapshot->unpacked_with_version = $lastWaterDrawingPaperworkUnpackingVersion;
if (!$this->fetchTable('Snapshots')->save($unpackable_water_drawing_paperwork_snapshot)) {
ConnectionManager::get('default')->rollback();
$io->abort("\nError saving snapshot: " . json_encode($unpackable_water_drawing_paperwork_snapshot->getErrors()));
}
$processed++;
$io->success("\nSUCCESSFULLY PROCESSED: $processed !\n");
}
$this->fetchTable('WaterDrawingPaperworks')->addBehavior('Muffin/Trash.Trash');
$io->success("\nJOB IS DONE. REQUESTED: $limit, UNPACKABLE: " . count($unpackable_water_drawing_paperwork_snapshots) . " SUCCESSFULLY PROCESSED: $processed");
if (count($unpackable_water_drawing_paperwork_snapshots) > 0) ConnectionManager::get('default')->commit();
return static::CODE_SUCCESS;
}
}

250
idrocap_wa/src/Console/Installer.php

@ -0,0 +1,250 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Console;
if (!defined('STDIN')) {
define('STDIN', fopen('php://stdin', 'r'));
}
use Cake\Codeception\Console\Installer as CodeceptionInstaller;
use Cake\Utility\Security;
use Composer\IO\IOInterface;
use Composer\Script\Event;
use Exception;
/**
* Provides installation hooks for when this application is installed through
* composer. Customize this class to suit your needs.
*/
class Installer
{
/**
* An array of directories to be made writable
*/
public const WRITABLE_DIRS = [
'logs',
'tmp',
'tmp/cache',
'tmp/cache/models',
'tmp/cache/persistent',
'tmp/cache/views',
'tmp/sessions',
'tmp/tests',
];
/**
* Does some routine installation tasks so people don't have to.
*
* @param \Composer\Script\Event $event The composer event object.
* @throws \Exception Exception raised by validator.
* @return void
*/
public static function postInstall(Event $event): void
{
$io = $event->getIO();
$rootDir = dirname(__DIR__, 2);
static::createAppLocalConfig($rootDir, $io);
static::createWritableDirectories($rootDir, $io);
static::setFolderPermissions($rootDir, $io);
static::setSecuritySalt($rootDir, $io);
if (class_exists(CodeceptionInstaller::class)) {
CodeceptionInstaller::customizeCodeceptionBinary($event);
}
}
/**
* Create config/app_local.php file if it does not exist.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function createAppLocalConfig(string $dir, IOInterface $io): void
{
$appLocalConfig = $dir . '/config/app_local.php';
$appLocalConfigTemplate = $dir . '/config/app_local.example.php';
if (!file_exists($appLocalConfig)) {
copy($appLocalConfigTemplate, $appLocalConfig);
$io->write('Created `config/app_local.php` file');
}
}
/**
* Create the `logs` and `tmp` directories.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function createWritableDirectories(string $dir, IOInterface $io): void
{
foreach (static::WRITABLE_DIRS as $path) {
$path = $dir . '/' . $path;
if (!file_exists($path)) {
mkdir($path);
$io->write('Created `' . $path . '` directory');
}
}
}
/**
* Set globally writable permissions on the "tmp" and "logs" directory.
*
* This is not the most secure default, but it gets people up and running quickly.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function setFolderPermissions(string $dir, IOInterface $io): void
{
// ask if the permissions should be changed
if ($io->isInteractive()) {
$validator = function (string $arg): string {
if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
return $arg;
}
throw new Exception('This is not a valid answer. Please choose Y or n.');
};
$setFolderPermissions = $io->askAndValidate(
'<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
$validator,
10,
'Y'
);
if (in_array($setFolderPermissions, ['n', 'N'])) {
return;
}
}
// Change the permissions on a path and output the results.
$changePerms = function (string $path) use ($io): void {
$currentPerms = fileperms($path) & 0777;
$worldWritable = $currentPerms | 0007;
if ($worldWritable == $currentPerms) {
return;
}
$res = chmod($path, $worldWritable);
if ($res) {
$io->write('Permissions set on ' . $path);
} else {
$io->write('Failed to set permissions on ' . $path);
}
};
$walker = function (string $dir) use (&$walker, $changePerms): void {
/** @phpstan-ignore-next-line */
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
if (!is_dir($path)) {
continue;
}
$changePerms($path);
$walker($path);
}
};
$walker($dir . '/tmp');
$changePerms($dir . '/tmp');
$changePerms($dir . '/logs');
}
/**
* Set the security.salt value in the application's config file.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function setSecuritySalt(string $dir, IOInterface $io): void
{
$newKey = hash('sha256', Security::randomBytes(64));
static::setSecuritySaltInFile($dir, $io, $newKey, 'app_local.php');
}
/**
* Set the security.salt value in a given file
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @param string $newKey key to set in the file
* @param string $file A path to a file relative to the application's root
* @return void
*/
public static function setSecuritySaltInFile(string $dir, IOInterface $io, string $newKey, string $file): void
{
$config = $dir . '/config/' . $file;
$content = file_get_contents($config);
/** @phpstan-ignore-next-line */
$content = str_replace('__SALT__', $newKey, $content, $count);
if ($count == 0) {
$io->write('No Security.salt placeholder to replace.');
return;
}
$result = file_put_contents($config, $content);
if ($result) {
$io->write('Updated Security.salt value in config/' . $file);
return;
}
$io->write('Unable to update Security.salt value.');
}
/**
* Set the APP_NAME value in a given file
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @param string $appName app name to set in the file
* @param string $file A path to a file relative to the application's root
* @return void
*/
public static function setAppNameInFile(string $dir, IOInterface $io, string $appName, string $file): void
{
$config = $dir . '/config/' . $file;
$content = file_get_contents($config);
/** @phpstan-ignore-next-line */
$content = str_replace('__APP_NAME__', $appName, $content, $count);
if ($count == 0) {
$io->write('No __APP_NAME__ placeholder to replace.');
return;
}
$result = file_put_contents($config, $content);
if ($result) {
$io->write('Updated __APP_NAME__ value in config/' . $file);
return;
}
$io->write('Unable to update __APP_NAME__ value.');
}
}

91
idrocap_wa/src/Controller/AiServicesController.php

@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Model\Entity\Attachment;
use Cake\Core\Configure;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Response;
use Cake\View\JsonView;
use Exception;
use Cake\Http\Client;
class AiServicesController extends AppController
{
private Client $client;
public function initialize(): void
{
parent::initialize();
$this->viewBuilder()->setClassName(JsonView::class);
$this->client = new Client([
'timeout' => 120
]);
}
public function autocomplete(): Response
{
if (!$this->request->is('post')) {
$this->respond(405, "Method not allowed");
}
$isAiServiceEnabled = Configure::read('App.aiServiceEnabled');
if(!$isAiServiceEnabled) {
return $this->respond(403, "AI Service is disabled");
}
$request = $this->request->getData();
$type = $request['type'] ?? null;
$documentHash = $request['documentHash'] ?? null;
if (!$type || !$documentHash) {
return $this->respond(400, "Missing parameters");
}
$attachment = $this->findAttachment($documentHash);
if (!$attachment) {
return $this->respond(404, "Attachment not found");
}
$file_name = $attachment->file_name;
$container_co_id = $attachment->container_controllable_object_id;
$path = Configure::read('App.attachmentsPath') . "/" . $container_co_id . "/" . $file_name;
$output = \App\WGS\FileStorage\FileStorageFactory::create()->getFile($path);
if ($output === false) {
return $this->respond(404, "File not found or could not be opened");
}
return $this->callAiServiceAndRespond($request['type'], $request['documentHash'], $output);
}
private function callAiServiceAndRespond(string $type, string $documentHash, string $fileContent): Response
{
try {
$postData = json_encode([
'type' => $type,
'documentHash' => $documentHash,
'fileContent' => base64_encode($fileContent),
]);
$apiResponse = $this->client->post(Configure::read('App.aiServiceUrl'),$postData, ['type' => "json"]);
if ($apiResponse->isOk()) {
return $this->respond(200, ['result' => $apiResponse->getJson()]);
} else {
return $this->respond($apiResponse->getStatusCode(), "API Error: " . $apiResponse->getStatusCode());
}
} catch (Exception $e) {
return $this->respond(500, $e->getMessage());
}
}
private function findAttachment(string $documentHash): Attachment
{
return $this->fetchTable('Attachments')->find()
->contain(['ContainerControllableObjects'])
->where(['file_name' => $documentHash])
->first();
}
}

74
idrocap_wa/src/Controller/Api/AppController.php

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.2.9
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller\Api;
use Cake\Controller\Controller;
use Cake\Http\Response;
use App\Model\Entity\User;
/**
* Application Controller
*
* Add your application-wide methods in the class below, your controllers
* will inherit them.
*
* @link https://book.cakephp.org/4/en/controllers.html#the-app-controller
*/
class AppController extends Controller
{
protected $nopaginate;
public ?User $logged_user = null;
/**
* Initialization hook method.
*
* Use this method to add common initialization code like loading components.
*
* e.g. `$this->loadComponent('FormProtection');`
*
* @return void
*/
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Authentication.Authentication');
$this->logged_user = $this->Authentication->getIdentity();
$this->nopaginate = $this->request->getQuery('nopaginate') !== null && $this->request->getQuery('nopaginate');
}
/**
* respond
*
* @param Int $code
* @param null|Object|Array|String $bodyResponse
* @param String $content
* @return Response
*/
public function respond(Int $code = 200, null|Object|Array|String $bodyResponse = null, String $content = "application/json") : Response
{
$bodyResponse = is_object($bodyResponse) || is_array($bodyResponse) ? $bodyResponse : ['result' => $bodyResponse];
$response = $this->getResponse();
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
$response = $response->withHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PUT');
$response = $response->withHeader('Access-Control-Max-Age', '60');
$response = $response->withHeader('Access-Control-Allow-Headers', 'x-requested-with, Content-Type, origin, authorization, accept, client-security-token');
return $response->withStringBody(json_encode($bodyResponse))->withStatus($code)->withType($content);
}
}

53
idrocap_wa/src/Controller/Api/AttachmentsController.php

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
use Cake\Core\Configure;
/**
* Attachments Controller
*
* @property \App\Model\Table\AttachmentsTable $Attachments
* @method \App\Model\Entity\Attachment[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class AttachmentsController extends AppController
{
public function index() {return $this->respond(400, "not implemented");}
public function add() {return $this->respond(400, "not implemented");}
public function edit($id = null) {return $this->respond(400, "not implemented");}
public function delete($id = null) {return $this->respond(400, "not implemented");}
/**
* View method
*
* @param string|null $hash Attachment hash.
* @return \Cake\Http\Response|void
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($hash)
{
$attachment = $this->fetchTable('Attachments')->find()
->contain(['ContainerControllableObjects'])
->where(['file_name' => $hash])
->first();
if (!$attachment) return $this->respond(404, __('Allegato non trovato'));
if (!$this->logged_user->canHandleAttachmentsForControllableObject($attachment->container_controllable_object)) return $this->respond(403, __('Non hai i permessi necessari per visionare l\'allegato'));
$mimetype = $attachment->mimetype;
$file_name = $attachment->file_name;
$original_file_name = $attachment->original_file_name;
$container_co_id = $attachment->container_controllable_object_id;
$path = Configure::read('App.attachmentsPath') . "/" . $container_co_id . "/" . $file_name;
$output = \App\WGS\FileStorage\FileStorageFactory::create()->getFile($path);
$this->response = $this->response
->withAddedHeader('Content-Disposition', 'filename="' . $original_file_name . '"')
->withCharset('UTF-8')
->withType($mimetype)
->withStringBody($output);
return $this->response;
}
}

70
idrocap_wa/src/Controller/Api/ErrorController.php

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.3.4
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller\Api;
use Cake\Event\EventInterface;
/**
* Error Handling Controller
*
* Controller used by ExceptionRenderer to render error responses.
*/
class ErrorController extends AppController
{
/**
* Initialization hook method.
*
* @return void
*/
public function initialize(): void
{
parent::initialize();
}
/**
* beforeFilter callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function beforeFilter(EventInterface $event)
{
}
/**
* beforeRender callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function beforeRender(EventInterface $event)
{
parent::beforeRender($event);
$this->viewBuilder()->setTemplatePath('Error');
}
/**
* afterFilter callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function afterFilter(EventInterface $event)
{
}
}

156
idrocap_wa/src/Controller/Api/NotificationsController.php

@ -0,0 +1,156 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
use Cake\Core\Configure;
use Cake\I18n\DateTime;
/**
* Notifications Controller
*
* @property \App\Model\Table\NotificationsTable $Notifications
* @method \App\Model\Entity\Notification[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class NotificationsController extends AppController
{
public function view($id = null) {return $this->respond(400, "not implemented");}
public function delete($id = null) {return $this->respond(400, "not implemented");}
/**
* index
*
* @return \Cake\Http\Response
*/
public function index()
{
$notifications = $this->fetchTable('Notifications')->find()
->contain(['NotificationTypes'])
->where(['user_id' => $this->logged_user->id, 'read_by_user IS NULL'])
->order(['created' => 'DESC'])
->formatResults(function (\Cake\Collection\CollectionInterface $notifications) {
return $notifications
->map(function ($notification) {
if ($notification->already_mapped) return $notification;
$notification->already_mapped = true;
$notification->setHidden(array_merge($notification->getHidden(), ['already_mapped']));
$notification->info = json_decode($notification->info);
$webURL = Configure::read('App.weburl');
$notification->info->weblink = $webURL . $notification->info->link;
$notification->info->link = '/api' . $notification->info->link;
return $notification;
});
});
/**
* Default pagination settings.
*
* When calling paginate() these settings will be merged with the configuration
* you provide.
*
* - `maxLimit` - The maximum limit users can choose to view. Defaults to 100
* - `limit` - The initial number of items per page. Defaults to 20.
* - `page` - The starting page, defaults to 1.
* - `allowedParameters` - A list of parameters users are allowed to set using request
* parameters. Modifying this list will allow users to have more influence
* over pagination, be careful with what you permit.
* - `sortableFields` - A list of fields which can be used for sorting. By
* default all table columns can be used for sorting. You can use this option
* to restrict sorting only by particular fields. If you want to allow
* sorting on either associated columns or calculated fields then you will
* have to explicitly specify them (along with other fields). Using an empty
* array will disable sorting alltogether.
* - `finder` - The table finder to use. Defaults to `all`.
* - `scope` - If specified this scope will be used to get the paging options
* from the query params passed to paginate(). Scopes allow namespacing the
* paging options and allows paginating multiple models in the same action.
* Default `null`.
*
* protected array $_defaultConfig = [
* 'page' => 1,
* 'limit' => 20,
* 'maxLimit' => 100,
* 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
* 'sortableFields' => null,
* 'finder' => 'all',
* 'scope' => null,
* ];
*/
try {
$paginated_notifications = $this->paginate($notifications, [
'maxLimit' => 100,
'limit' => 100,
]);
return $this->respond(200, ['notifications' => $paginated_notifications, 'pagination' => $paginated_notifications->pagingParams()]);
} catch (\Cake\Http\Exception\NotFoundException $th) {
return $this->respond(404, ['result' => $th->getMessage()]);
}
}
/**
* count
*
* @return \Cake\Http\Response
*/
public function count()
{
$notifications_count = $this->fetchTable('Notifications')->find()
->where(['user_id' => $this->logged_user->id, 'read_by_user IS NULL'])
->count();
return $this->respond(200, ['count' => $notifications_count]);
}
/**
* add
*
* @return \Cake\Http\Response
*/
public function add()
{
if (!$this->logged_user->hasCapability(['notifications.read_all'])) {
return $this->respond(403, ['capabilities' => ['notifications.read_all']]);
}
$userId = $this->logged_user->id;
$this->fetchTable('Notifications')->updateAll(['read_by_user' => DateTime::now()], ['read_by_user IS NULL', 'user_id' => $userId]);
return $this->respond(201, "all notifications has been marked as read");
}
/**
* edit
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
$notification = $this->fetchTable('Notifications')->get($id);
$userId = $this->logged_user->id;
if($notification->user_id != $userId)
{
return $this->respond(403, "access unauthorised");
}
if($notification->read_by_user)
{
return $this->respond(400, "notification has already been read");
}
$notification->read_by_user = DateTime::now();
if(!$this->fetchTable('Notifications')->save($notification))
{
return $this->respond(400, ['error' => $notification->getErrors()]);
}
$notification->info = json_decode($notification->info);
return $this->respond(201, ['notification' => $notification]);
}
public function check()
{
$notifications_count = $this->fetchTable('Notifications')->find()
->where(['user_id' => $this->logged_user->id, 'read_by_user IS NULL'])
->count();
return $this->respond(200, ['result' => $notifications_count != 0]);
}
}

31
idrocap_wa/src/Controller/Api/OrganisationTypesController.php

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
/**
* OrganisationTypes Controller
*
* @property \App\Model\Table\OrganisationTypesTable $OrganisationTypes
* @method \App\Model\Entity\OrganisationType[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class OrganisationTypesController extends AppController
{
public function add($id = null) {return $this->respond(400, "not implemented");}
public function view($id = null) {return $this->respond(400, "not implemented");}
public function edit($id = null) {return $this->respond(400, "not implemented");}
public function delete($id = null) {return $this->respond(400, "not implemented");}
/**
* index
*
* @return \Cake\Http\Response
*/
public function index()
{
$organisationTypes = $this->fetchTable('OrganisationTypes')->find('list')->order(['OrganisationTypes.description' => 'ASC']);
return $this->respond(200, ['organisation_types' => $organisationTypes]);
}
}

157
idrocap_wa/src/Controller/Api/OrganisationsController.php

@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
use Cake\Core\Configure;
/**
* Organisations Controller
*
* @property \App\Model\Table\OrganisationsTable $Organisations
* @method \App\Model\Entity\Organisation[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class OrganisationsController extends AppController
{
public function add() {return $this->respond(400, "not implemented");}
public function edit($id = null) {return $this->respond(400, "not implemented");}
public function delete($id = null) {return $this->respond(400, "not implemented");}
/**
* index
*
* @return \Cake\Http\Response
*/
public function index()
{
$options = ['type' => $this->request->getQuery('type') ?? null];
$user_can_index = $this->fetchTable('Organisations')->can_index($this->logged_user, $options);
if (!$user_can_index)
{
return $this->respond(403, $options['error']);
}
$organisations = $this->fetchTable('Organisations')
->find($options['finder'], params: $options)
->contain(['Actors', 'OrganisationTypes'])
->formatResults(function (\Cake\Collection\CollectionInterface $organisations) {
return $organisations
->map(function ($organisation) {
if ($organisation->already_mapped) return $organisation;
$organisation->already_mapped = true;
$organisation->setHidden(array_merge($organisation->getHidden(), ['already_mapped', 'phone', 'mobile', 'fax', 'email', 'organisation_type_id', 'actor_id', 'incident_creator', 'pec', 'deleted', 'incident_default_tab', 'loggable_object_id', 'controllable_object_interface_id']));
$organisation->actor->setHidden(array_merge($organisation->actor->getHidden(), ['actor_type_id', 'interoperability_identifier', 'incident_type_group_id', 'deleted']));
return $organisation;
});
});
$organisations = $this->fetchTable('Organisations')->applySearchParams($organisations, $this->request->getQueryParams());
/**
* Default pagination settings.
*
* When calling paginate() these settings will be merged with the configuration
* you provide.
*
* - `maxLimit` - The maximum limit users can choose to view. Defaults to 100
* - `limit` - The initial number of items per page. Defaults to 20.
* - `page` - The starting page, defaults to 1.
* - `allowedParameters` - A list of parameters users are allowed to set using request
* parameters. Modifying this list will allow users to have more influence
* over pagination, be careful with what you permit.
* - `sortableFields` - A list of fields which can be used for sorting. By
* default all table columns can be used for sorting. You can use this option
* to restrict sorting only by particular fields. If you want to allow
* sorting on either associated columns or calculated fields then you will
* have to explicitly specify them (along with other fields). Using an empty
* array will disable sorting alltogether.
* - `finder` - The table finder to use. Defaults to `all`.
* - `scope` - If specified this scope will be used to get the paging options
* from the query params passed to paginate(). Scopes allow namespacing the
* paging options and allows paginating multiple models in the same action.
* Default `null`.
*
* protected array $_defaultConfig = [
* 'page' => 1,
* 'limit' => 20,
* 'maxLimit' => 100,
* 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
* 'sortableFields' => null,
* 'finder' => 'all',
* 'scope' => null,
* ];
*/
try {
$paginated_organisations = $this->paginate($organisations, [
'maxLimit' => 100,
'limit' => 100,
'sortableFields' => ['Actors.description', 'OrganisationTypes.description', 'acronym', 'address', 'district', 'cap', 'province'],
'order' => [
'Actors.description' => 'ASC'
],
]);
return $this->respond(200, ['organisations' => $paginated_organisations, 'pagination' => $paginated_organisations->pagingParams()]);
} catch (\Cake\Http\Exception\NotFoundException $th) {
return $this->respond(404, ['result' => $th->getMessage()]);
}
}
/**
* view
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function view($id = null)
{
$options = [];
$organisation = $this->fetchTable('Organisations')->get($id, contain: ['Actors', 'OrganisationTypes']);
$organisation->setHidden(array_merge($organisation->getHidden(), ['phone', 'mobile', 'fax', 'email', 'organisation_type_id', 'actor_id', 'incident_creator', 'pec', 'deleted', 'incident_default_tab', 'loggable_object_id', 'controllable_object_interface_id']));
$organisation->actor->setHidden(array_merge($organisation->actor->getHidden(), ['actor_type_id', 'interoperability_identifier', 'incident_type_group_id', 'deleted']));
if (($this->logged_user->organisation_id == $id) && (!$organisation->can_view($this->logged_user, $options))) {
return $this->respond(403, $options['error']);
}
$organisation->deliveries = $organisation->actor->getContactsList();
return $this->respond(200, ['organisation' => $organisation]);
}
/**
* getPhoto
*
* @param String $file_name
* @return \Cake\Http\Response
*/
public function getPhoto($file_name)
{
$options = [];
$organisation = $this->fetchTable('Organisations')->find()
->where(['Organisations.photo' => $file_name])
->first();
if (!$organisation) {
return $this->respond(404, __('Organizzazione non trovata!'));
}
if (!$organisation->can_view($this->logged_user, $options)) {
return $this->respond(403, $options['error']);
}
$mimetype = "image/jpeg";
$path = Configure::read('App.organisationPhotoPath') . "/" . $file_name;
$output = \App\WGS\FileStorage\FileStorageFactory::create()->getFile($path);
$this->response = $this->response
->withType($mimetype)
->withStringBody($output);
return $this->response;
}
}

67
idrocap_wa/src/Controller/Api/SubscriptionsController.php

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
/**
* Subscriptions Controller
*
* @property \App\Model\Table\PushNotificationsTable $PushNotifications
* @method \App\Model\Entity\PushNotification[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class SubscriptionsController extends AppController
{
public function add() {return $this->respond(400, "not implemented");}
public function view($id = null) {return $this->respond(400, "not implemented");}
public function edit($id = null) {return $this->respond(400, "not implemented");}
public function delete($id = null) {return $this->respond(400, "not implemented");}
/**
* subscribe
*
* @return \Cake\Http\Response
*/
public function subscribe()
{
$token = $this->request->getData('token') ?? null;
if (empty($token)) {
return $this->respond(400, ['error' => 'invalid subscribe token']);
}
$push_notification = $this->fetchTable('PushNotifications')
->newEntity([
'value' => $token,
'enable_notifications' => true,
'delivery' => [
'actor_id' => $this->logged_user->actor_id,
'delivery_type_id' => 0,
],
]);
$result = $this->fetchTable('PushNotifications')->save($push_notification);
$status_code = is_object($result) && get_class($result) === "App\Model\Entity\PushNotification" ? 201 : 400;
$message = $status_code == 201 ? $push_notification : $push_notification->getErrors();
return $this->respond($status_code, ['result' => $message]);
}
/**
* unsubscribe
*
* @return \Cake\Http\Response
*/
public function unsubscribe()
{
$user_push_notification_tokens = $this->fetchTable('Deliveries')
->find()
->where(['Deliveries.actor_id' => $this->logged_user->actor_id, 'Deliveries.delivery_type_id' => 0])
->all();
foreach ($user_push_notification_tokens as $user_push_notification_token) {
if (!$this->fetchTable('Deliveries')->delete($user_push_notification_token)) {
return $this->respond(500, ['result' => $user_push_notification_token->getErrors()]);
}
}
return $this->respond(200, ['unsubscribed' => $user_push_notification_tokens->count()]);
}
}

230
idrocap_wa/src/Controller/Api/UsersController.php

@ -0,0 +1,230 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
use Cake\Event\EventInterface;
use Firebase\JWT\JWT;
use Cake\Core\Configure;
/**
* Users Controller
*
* @property \App\Model\Table\UsersTable $Users
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class UsersController extends AppController
{
public function beforeFilter(EventInterface $event)
{
$this->Authentication->allowUnauthenticated(['login']);
}
public function add() {return $this->respond(400, "not implemented");}
public function edit($id = null) {return $this->respond(400, "not implemented");}
public function delete($id = null) {return $this->respond(400, "not implemented");}
/**
* index
*
* @return \Cake\Http\Response
*/
public function index()
{
$options = [];
$user_can_index = $this->fetchTable('Users')->can_index($this->logged_user, $options);
if (!$user_can_index)
{
return $this->respond(403, $options['error']);
}
$users = $this->fetchTable('Users')
->find($options['finder'], params: $options)
->contain(['UserActors', 'Organisations' => ['Actors']])
->formatResults(function (\Cake\Collection\CollectionInterface $users) {
return $users
->map(function ($user) {
if ($user->already_mapped) return $user;
$user->already_mapped = true;
$user->setHidden(array_merge($user->getHidden(), ['already_mapped', 'organisation_id', 'phone', 'mobile', 'mobile2', 'email', 'notification_hash', 'fax', 'change_password', 'last_login', 'last_registered_request', 'from_mobile', 'last_ip_address', 'language_id', 'privacy', 'conditions', 'password_recovery_token', 'last_change_password', 'password_recovery_counter', 'email2', 'actor_id', 'ambiguity_verified', 'invalid_login_date', 'invalid_login_count', 'deleted', 'loggable_object_id', 'email2_wd']));
$user->user_actor->setHidden(array_merge($user->user_actor->getHidden(), ['actor_type_id', 'interoperability_identifier', 'incident_type_group_id', 'deleted']));
$user->organisation_description = $user->organisation->actor->description;
$user->birthday = $user->birthday?->format('d/m/Y');
return $user;
});
});
$users = $this->fetchTable('Users')->applySearchParams($users, $this->request->getQueryParams());
/**
* Default pagination settings.
*
* When calling paginate() these settings will be merged with the configuration
* you provide.
*
* - `maxLimit` - The maximum limit users can choose to view. Defaults to 100
* - `limit` - The initial number of items per page. Defaults to 20.
* - `page` - The starting page, defaults to 1.
* - `allowedParameters` - A list of parameters users are allowed to set using request
* parameters. Modifying this list will allow users to have more influence
* over pagination, be careful with what you permit.
* - `sortableFields` - A list of fields which can be used for sorting. By
* default all table columns can be used for sorting. You can use this option
* to restrict sorting only by particular fields. If you want to allow
* sorting on either associated columns or calculated fields then you will
* have to explicitly specify them (along with other fields). Using an empty
* array will disable sorting alltogether.
* - `finder` - The table finder to use. Defaults to `all`.
* - `scope` - If specified this scope will be used to get the paging options
* from the query params passed to paginate(). Scopes allow namespacing the
* paging options and allows paginating multiple models in the same action.
* Default `null`.
*
* protected array $_defaultConfig = [
* 'page' => 1,
* 'limit' => 20,
* 'maxLimit' => 100,
* 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
* 'sortableFields' => null,
* 'finder' => 'all',
* 'scope' => null,
* ];
*/
try {
$paginated_users = $this->paginate($users, [
'maxLimit' => 100,
'limit' => 100,
'sortableFields' => ['UserActors.description', 'username', 'surname', 'name', 'cap', 'gender'],
'order' => [
'Users.surname' => 'ASC',
],
]);
return $this->respond(200, ['users' => $paginated_users, 'pagination' => $paginated_users->pagingParams()]);
} catch (\Cake\Http\Exception\NotFoundException $th) {
return $this->respond(404, ['result' => $th->getMessage()]);
}
}
/**
* view
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function view($id)
{
$options = [];
$user = $this->fetchTable('Users')->get($id, contain: ['UserActors', 'Organisations' => ['Actors', 'OrganisationTypes']]);
if ($user->id != $this->logged_user->id && (!$user->can_view($this->logged_user, $options))) {
return $this->respond(403, $options['error']);
}
$user->setHidden(array_merge($user->getHidden(), ['fax', 'email', 'mobile2', 'mobile', 'email2', 'phone', 'organisation_id', 'notification_hash', 'change_password', 'last_login', 'last_registered_request', 'from_mobile', 'last_ip_address', 'language_id', 'privacy', 'conditions', 'password_recovery_token', 'last_change_password', 'password_recovery_counter', 'actor_id', 'ambiguity_verified', 'invalid_login_date', 'invalid_login_count', 'deleted', 'loggable_object_id', 'email2_wd']));
$user->actor = $user->user_actor;
unset($user->user_actor);
$user->actor->setHidden(array_merge($user->actor->getHidden(), ['actor_type_id', 'interoperability_identifier', 'incident_type_group_id', 'deleted']));
$user->organisation_description = null;
if ($user->hasValue('organisation')) {
$user->organisation->setHidden(array_merge($user->organisation->getHidden(), ['phone', 'mobile', 'fax', 'email', 'organisation_type_id', 'actor_id', 'incident_creator', 'pec', 'deleted', 'incident_default_tab', 'loggable_object_id', 'controllable_object_interface_id']));
$user->organisation_description = $user->organisation->actor->description;
unset($user->organisation->actor);
}
$user->deliveries = $user->actor->getContactsList();
return $this->respond(200, ['user' => $user]);
}
/**
* login
*
* @return \Cake\Http\Response
*/
public function login()
{
$result = $this->Authentication->getResult();
// If the user is logged in send them away.
if ($result->isValid()) {
$receivedData = $this->getRequest()->getData();
$client_id = isset($receivedData['client_id']) && $receivedData['client_id'] != "" ? $receivedData['client_id'] : 'auto-' . bin2hex(random_bytes(32));
$platform = isset($receivedData['platform']) ? $receivedData['platform'] : 'unknown';
$tokenExpirationTime = Configure::read('App.jwt.expiration_time');
$token = JWT::encode([
'sub' => $this->Authentication->getIdentity()->get('username'),
'user_id' => $this->Authentication->getIdentity()->get('id'),
'exp' => time() + $tokenExpirationTime,
'client_id' => $client_id,
'platform' => $platform,
], file_get_contents(Configure::read('App.jwt.keys.private')), Configure::read('App.jwt.algorithm'));
$user = $this->fetchTable('Users')->get($this->Authentication->getIdentity()->get('id'));
$user->setHidden(['password', 'phone', 'mobile', 'mobile2', 'email', 'mobile_token_date', 'mobile_token', 'notification_hash', 'fax', 'change_password', 'last_login', 'last_registered_request', 'from_mobile', 'last_ip_address', 'position', 'position_date', 'ask_new_password', 'password_expiration', 'password_recovery_token', 'password_recovery_counter', 'email2', 'ambiguity_verified', 'invalid_login_date', 'invalid_login_count', 'deleted', 'loggable_object_id', 'email2_wd', 'make_volunteer', 'full_name', 'capabilities']);
$user->mobile_menu = $this->fetchTable('MobileComponents')
->find()
->matching('Capabilities')
->where(['Capabilities.id IN' => $user->getCapabilityIds(true)])
->order(['MobileComponents.id' => 'ASC'])
->distinct(['MobileComponents.menu_item'])
->select('MobileComponents.menu_item')
->toArray();
$user->notifications = $this->fetchTable('Notifications')->find()
->contain(['NotificationTypes'])
->where(['user_id' => $user->id, 'read_by_user IS NULL'])
->order(['created' => 'DESC'])
->formatResults(function (\Cake\Collection\CollectionInterface $notifications) {
return $notifications
->map(function ($notification) {
$notification->info = json_decode($notification->info);
$webURL = Configure::read('App.weburl');
$notification->info->weblink = $webURL . $notification->info->link;
$notification->info->link = '/api' . $notification->info->link;
return $notification;
});
})->count() != 0;
return $this->respond(200, ['token' => $token, 'user' => $user, 'mobile_menu' => $user->mobile_menu]);
}
if ($this->request->is('post')) {
return $this->respond(401, ['error' => 'invalid credentials']);
}
}
/**
* getPhoto
*
* @param String $file_name
* @return \Cake\Http\Response
*/
public function getPhoto($file_name)
{
$options = [];
$user = $this->fetchTable('Users')->find()
->where(['Users.photo' => $file_name])
->first();
if (!$user) {
return $this->respond(404, __('Utente non trovato!'));
}
if (!$user->can_view($this->logged_user, $options)) {
return $this->respond(403, $options['error']);
}
$mimetype = "image/jpeg";
$path = Configure::read('App.organisationPhotoPath') . "/" . $file_name;
$output = \App\WGS\FileStorage\FileStorageFactory::create()->getFile($path);
$this->response = $this->response
->withType($mimetype)
->withStringBody($output);
return $this->response;
}
}

75
idrocap_wa/src/Controller/Api/WaterDrawingDerivationsController.php

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
use App\WGS\Geo\Geocoding;
use App\WGS\Geo\GeoValidation;
use App\WGS\SnapshotsHandler\SnapshotsHandler;
use Cake\Event\EventInterface;
use Firebase\JWT\JWT;
use Cake\Core\Configure;
use Cake\I18n\DateTime;
/**
* WaterDrawingDerivations Controller
*
* @property \App\Model\Table\WaterDrawingDerivationsTable $UseWaterDrawingDerivationsrs
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class WaterDrawingDerivationsController extends AppController
{
public function view($id){
$waterDrawingDerivation = $this->fetchTable('WaterDrawingDerivations')->find()->where(['WaterDrawingDerivations.id' => $id])->first();
if(!isset($waterDrawingDerivation)) return $this->respond(500, ['message' => 'Punto di derivazione non trovato.']);
return $this->respond(200, ['WaterDrawingDerivation' => $waterDrawingDerivation]);
}
public function update_data(){
$data = $this->request->getData();
$waterDrawingDerivation = $this->fetchTable('WaterDrawingDerivations')->find()->where(['WaterDrawingDerivations.id' => $data['water_drawing_derivation_id']])->first();
if(!isset($waterDrawingDerivation)) return $this->respond(500, ['message' => 'Punto di derivazione non trovato.']);
$waterDrawingDerivation = $this->fetchTable('WaterDrawingDerivations')->patchEntity($waterDrawingDerivation, [
'annual_volume' => $data['volume'],
'average_flow_rate' => $data['portata']
]);
if($this->fetchTable('WaterDrawingDerivations')->save($waterDrawingDerivation)){
SnapshotsHandler::createSnapshot($waterDrawingDerivation->water_drawing_paperwork->controllable_object_id);
return $this->respond(200, ['message' => $waterDrawingDerivation]);
}else{
return $this->respond(500, ['message' => 'KO', 'error' => $waterDrawingDerivation->getErrors()]);
}
}
public function update_location(){
$data = $this->request->getData();
$waterDrawingDerivation = $this->fetchTable('WaterDrawingDerivations')->find()->contain(['WaterDrawingPaperworks'])->where(['WaterDrawingDerivations.id' => $data['water_drawing_derivation_id']])->first();
if(!isset($waterDrawingDerivation)) return $this->respond(500, ['message' => 'Punto di derivazione non trovato.']);
$feature_collection = '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[' . $data['longitude'] . ',' . $data['latitude'] . ']},"properties":null}]}';
if(!GeoValidation::isValidGeometry(json_decode($feature_collection))) return $this->respond(500, ['message' => 'Punto di derivazione non valido.']);
$reverse_geocoding = Geocoding::performGeocoding('reverse', ['format' => 'json', 'namedetails' => '1', 'lat' => $data['latitude'], 'lon' => $data['longitude']]);
if($reverse_geocoding['status_code']['http_code'] == '200'){
$waterDrawingDerivation = $this->fetchTable('WaterDrawingDerivations')->patchEntity($waterDrawingDerivation, [
'latitude' => $data['latitude'],
'longitude' => $data['longitude'],
'feature_collection' => '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[' . $data['longitude'] . ',' . $data['latitude'] . ']},"properties":null}]}',
'location' => isset($reverse_geocoding['contents']['address']['road']) ? $reverse_geocoding['contents']['address']['road'] : '',
'district' => isset($reverse_geocoding['contents']['address']['town']) ? $reverse_geocoding['contents']['address']['town'] : '',
'description' => isset($reverse_geocoding['contents']['display_name']) ? $reverse_geocoding['contents']['display_name'] : ''
]);
}else{
$waterDrawingDerivation = $this->fetchTable('WaterDrawingDerivations')->patchEntity($waterDrawingDerivation, [
'latitude' => $data['latitude'],
'longitude' => $data['longitude'],
'feature_collection' => '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[' . $data['longitude'] . ',' . $data['latitude'] . ']},"properties":null}]}'
]);
}
if($this->fetchTable('WaterDrawingDerivations')->save($waterDrawingDerivation)){
SnapshotsHandler::createSnapshot($waterDrawingDerivation->water_drawing_paperwork->controllable_object_id);
return $this->respond(200, ['message' => 'OK']);
}else{
return $this->respond(500, ['message' => 'KO', 'error' => $waterDrawingDerivation->getErrors()]);
}
}
}

47
idrocap_wa/src/Controller/Api/WaterDrawingMeasurementsController.php

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
use App\WGS\SnapshotsHandler\SnapshotsHandler;
use Cake\Event\EventInterface;
use Firebase\JWT\JWT;
use Cake\Core\Configure;
use Cake\I18n\DateTime;
/**
* WaterDrawingMeasurements Controller
*
* @property \App\Model\Table\WaterDrawingMeasurementsTable $WaterDrawingMeasurements
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class WaterDrawingMeasurementsController extends AppController
{
public function add(){
$data = $this->request->getData();
$WaterDrawingMeasurement = $this->fetchTable('WaterDrawingMeasurements')->newEntity([
'volume' => $data['volume'],
'water_drawing_meter_id' => $data['water_drawing_meter_id'],
'date' => DateTime::now(),
'user_id' => $this->logged_user->id
]);
if($this->fetchTable('WaterDrawingMeasurements')->save($WaterDrawingMeasurement)){
$waterDrawingMeter = $this->fetchTable('WaterDrawingMeters')->get($data['water_drawing_meter_id'], contain: ['WaterDrawingIntendedUses' => ['WaterDrawingPaperworks']]);
SnapshotsHandler::createSnapshot($waterDrawingMeter->water_drawing_intended_use->water_drawing_paperwork->controllable_object_id);
return $this->respond(200, ['message' => __('Salvato con successo')]);
}else{
return $this->respond(500, ['message' => __('Errore durante il salvataggio.'), 'error' => $WaterDrawingMeasurement->getErrors()]);
}
}
public function last_index($id){
$WaterDrawingMeasurements = $this->fetchTable('WaterDrawingMeasurements')->find()->contain(['Users'])->where(['WaterDrawingMeasurements.water_drawing_meter_id' => $id])->order(['WaterDrawingMeasurements.date' => 'DESC', 'WaterDrawingMeasurements.id' => 'DESC'])->limit(5)->formatResults( function ($result){
return $result->map( function ($q) {
$q->date = $q->date?->format('d/m/Y');
return $q;
});
})->toArray();
return $this->respond(200, ['WaterDaringMeasurements' => $WaterDrawingMeasurements]);
}
}

170
idrocap_wa/src/Controller/Api/WaterDrawingPaperworksController.php

@ -0,0 +1,170 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Controller\Api\AppController;
use Cake\Event\EventInterface;
use Firebase\JWT\JWT;
use Cake\Core\Configure;
use Cake\I18n\DateTime;
/**
* Users Controller
*
* @property \App\Model\Table\UsersTable $Users
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class WaterDrawingPaperworksController extends AppController
{
public function index()
{
$options = [];
$user_can_index = $this->fetchTable('WaterDrawingPaperworks')->can_index($this->logged_user, $options);
if (!$user_can_index)
{
return $this->respond(403, $options['error']);
}
$waterDrawingPaperworks = $this->fetchTable('WaterDrawingPaperworks')->find()
->contain(['ControllableObjects', 'WaterDrawingPaperworkStatuses'])->matching('Applicants');
if ($this->logged_user->hasOnlyLowerPriorityCapability([
'documentation.water_drawing_paperworks.view',
'documentation.water_drawing_paperworks.view_own_province'
])) {
$waterDrawingPaperworks = $waterDrawingPaperworks->where(['authority_province' => $this->logged_user?->organisation_province]);
}
$waterDrawingPaperworks = $this->fetchTable('WaterDrawingPaperworks')->applySearchParams($waterDrawingPaperworks, $this->request->getQueryParams());
$waterDrawingPaperworks = $waterDrawingPaperworks->distinct(['WaterDrawingPaperworks.id'])->formatResults(function ($results){
return $results->map(function ($row) {
if ($row->already_mapped) return $row;
$row->already_mapped = true;
$row->setHidden(array_merge($row->getHidden(), ['already_mapped']));
$row->release_date = $row->release_date?->format('d/m/Y');
$row->expiration_date = $row->expiration_date?->format('d/m/Y');
$row->controllable_object->created = $row->controllable_object->created?->format('d/m/Y H:i:s');
$row->controllable_object->modified = $row->controllable_object->modified?->format('d/m/Y H:i:s');
return $row;
});
});
/**
* Default pagination settings.
*
* When calling paginate() these settings will be merged with the configuration
* you provide.
*
* - `maxLimit` - The maximum limit users can choose to view. Defaults to 100
* - `limit` - The initial number of items per page. Defaults to 20.
* - `page` - The starting page, defaults to 1.
* - `allowedParameters` - A list of parameters users are allowed to set using request
* parameters. Modifying this list will allow users to have more influence
* over pagination, be careful with what you permit.
* - `sortableFields` - A list of fields which can be used for sorting. By
* default all table columns can be used for sorting. You can use this option
* to restrict sorting only by particular fields. If you want to allow
* sorting on either associated columns or calculated fields then you will
* have to explicitly specify them (along with other fields). Using an empty
* array will disable sorting alltogether.
* - `finder` - The table finder to use. Defaults to `all`.
* - `scope` - If specified this scope will be used to get the paging options
* from the query params passed to paginate(). Scopes allow namespacing the
* paging options and allows paginating multiple models in the same action.
* Default `null`.
*
* protected array $_defaultConfig = [
* 'page' => 1,
* 'limit' => 20,
* 'maxLimit' => 100,
* 'allowedParameters' => ['limit', 'sort', 'page', 'direction'],
* 'sortableFields' => null,
* 'finder' => 'all',
* 'scope' => null,
* ];
*/
try {
$paginated_waterDrawingPaperworks = $this->paginate($waterDrawingPaperworks, [
'maxLimit' => 25,
'sortableFields' => ['id', 'WaterDrawingPaperworkStatuses.description', 'district', 'release_date', 'concession_duration', 'expiration_date', 'ControllableObjects.created', 'ControllableObjects.modified'],
]);
return $this->respond(200, ['waterDrawingPaperworks' => $paginated_waterDrawingPaperworks, 'pagination' => $paginated_waterDrawingPaperworks->pagingParams()]);
} catch (\Cake\Http\Exception\NotFoundException $th) {
return $this->respond(404, ['result' => $th->getMessage()]);
}
}
public function view($id){
$options = [];
$user_can_index = $this->fetchTable('WaterDrawingPaperworks')->can_index($this->logged_user, $options);
if (!$user_can_index)
{
return $this->respond(403, $options['error']);
}
$organisation = $this->getTableLocator()->get('Organisations')->find()->where(['Organisations.id' => $this->logged_user->organisation_id])->first();
$waterDrawingPaperwork = $this->fetchTable('WaterDrawingPaperworks')->find()
->contain([ 'WaterDrawingArticles', 'WaterDrawingFees', 'GcUsers' => ['Organisations'], 'DrarUsers' => ['Organisations'], 'WaterDrawingReturnPoints', 'WaterDrawingDerivations' => ['WaterDrawingDerivationTypes'], 'WaterDrawingPaperworkStatuses', 'ControllableObjects' => ['Attachments' => ['Tags']], 'Applicants',
'WaterDrawingAntimafiaCertificationRequests' =>
function ($q){
return $q->contain(['WaterDrawingAntimafiaCertificationRequestStatuses', 'Users'])->orderDesc('WaterDrawingAntimafiaCertificationRequests.created');
},
]);
if ($this->logged_user->hasOnlyLowerPriorityCapability([
'documentation.water_drawing_paperworks.view',
'documentation.water_drawing_paperworks.view_own_province'
])) {
$waterDrawingPaperwork = $waterDrawingPaperwork->where(['authority_province' => $this->logged_user?->organisation_province]);
}
$waterDrawingPaperwork = $waterDrawingPaperwork->where(['WaterDrawingPaperworks.id' => $id])
->formatResults(function (\Cake\Collection\CollectionInterface $results) use ($organisation){
return $results->map(function ($row) use ($organisation){
$row->can_edit = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.edit') && ( $this->logged_user->id == $row->gc_user_id && ($row->water_drawing_paperwork_status_id === -1 || $row->water_drawing_paperwork_status_id === -5 ) || ( $this->logged_user->id == $row->drar_user_id && $row->water_drawing_paperwork_status_id === -4 ));
$row->can_delete = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.delete');
$row->can_validate = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.validate') && $row->water_drawing_paperwork_status_id === -3;
$row->can_send_to_drar = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.edit') && $row->water_drawing_paperwork_status_id === -1 && $row->check_sdd && $row->gc_user_id == $this->logged_user->id;
$row->can_send_to_gc = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.edit') && $row->water_drawing_paperwork_status_id === -4 && $row->check_dec && $row->drar_user_id == $this->logged_user->id;
$row->can_assign_to_drar = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.assign') && $row->water_drawing_paperwork_status_id <= -2 && $organisation->organisation_type_id == 6;
$row->can_assign_to_gc = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.assign') && isset($row->gc_user->organisation->organisation_type_id) && $organisation->organisation_type_id == $row->gc_user->organisation->organisation_type_id;
if($row->water_drawing_paperwork_status_id === -5){
$row->can_add_fee = $this->logged_user->hasCapability('documentation.water_drawing_fees.add');
$row->can_edit_fee = $this->logged_user->hasCapability('documentation.water_drawing_fees.edit');
$row->can_view_fee = $this->logged_user->hasCapability('documentation.water_drawing_fees.view');
$row->can_add_payment = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.add_payment') ;
$row->can_view_payment = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.view_payment');
$row->can_add_meter = $this->logged_user->hasCapability('documentation.water_drawing_meters.add');
$row->can_add_measurements = $this->logged_user->hasCapability('documentation.water_drawing_measurements.add');
$row->can_view_measurements = true;
}
if($row->water_drawing_paperwork_status_id == -4){
$row->can_add_fee = $this->logged_user->hasCapability('documentation.water_drawing_fees.add');
$row->can_edit_fee = $this->logged_user->hasCapability('documentation.water_drawing_fees.edit');
$row->can_view_fee = $this->logged_user->hasCapability('documentation.water_drawing_fees.view');
$row->can_add_payment = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.add_payment') ;
$row->can_view_payment = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.view_payment');
$row->can_submit_antimafia_request = ($this->logged_user->id == $row->drar_user_id) && (!isset($row->water_drawing_antimafia_certification_requests[0]));
$row->can_antimafia_request_to_anac = ($this->logged_user->hasCapability('documentation.water_drawing_paperworks.antimafia_request')) && (isset($row->water_drawing_antimafia_certification_requests[0]) && ($row->water_drawing_antimafia_certification_requests[0]->water_drawing_antimafia_certification_request_status_id == 1));
$row->can_request_self_certification = ($this->logged_user->id == $row->drar_user_id) && (isset($row->water_drawing_antimafia_certification_requests[0]) && (new DateTime($row->water_drawing_antimafia_certification_requests[0]->created))->diffInDays(\Cake\I18n\DateTime::now()) > 30);
$row->can_upload_antimafia_attachment = (isset($row->water_drawing_antimafia_certification_requests[0]) && (
( ($this->logged_user->hasCapability('documentation.water_drawing_paperworks.antimafia_request')) && !($row->check_dam)) ||
( ($this->logged_user->id == $row->gc_user_id) && $row->water_drawing_antimafia_certification_requests[0]->water_drawing_antimafia_certification_request_status_id == 3)
));
}
return $row;
});
})->first();
$waterDrawingPaperwork->water_drawing_meters = $this->getTableLocator()->get('WaterDrawingMeters')->find()->contain(['WaterDrawingIntendedUses', 'WaterDrawingToolTypes', 'WaterDrawingMeasurements' => function ($q){
return $q->contain(['Users'])->orderDesc('WaterDrawingMeasurements.date')->limit(5);
}])->where(['WaterDrawingIntendedUses.water_drawing_paperwork_id' => $waterDrawingPaperwork->id, 'WaterDrawingMeters.removal_date IS NULL'])->formatResults(function ($results){
return $results->map(function ($row){
$row->installation_date = $row->installation_date?->format('d/m/Y');
return $row;
});
})->toArray();
return $this->respond(200, ['result' => $waterDrawingPaperwork]);
}
}

424
idrocap_wa/src/Controller/AppController.php

@ -0,0 +1,424 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.2.9
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Event\EventInterface;
use App\Model\Entity\User;
use App\WGS\NotificationsHandler\NotificationsHandler;
use Cake\Core\Configure;
use Cake\Http\Response;
use Cake\ORM\Query;
use Cake\I18n\DateTime;
use Cake\I18n\I18n;
use Exception;
/**
* Application Controller
*
* Add your application-wide methods in the class below, your controllers
* will inherit them.
*
* @link https://book.cakephp.org/4/en/controllers.html#the-app-controller
*/
class AppController extends Controller
{
public ?User $logged_user = null;
/**
* Initialization hook method.
*
* Use this method to add common initialization code like loading components.
*
* e.g. `$this->loadComponent('FormProtection');`
*
* @return void
*/
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Authentication.Authentication');
$this->loadComponent('Flash');
// recuperiamo l'entità dello user (se loggato):
$this->logged_user = $this->Authentication->getIdentity();
// settiamo il locale come da preferenze utente (se specificato, altrimenti ci pensa il LocaleSelectorMiddleware):
if (!empty($this->logged_user->language_id)) I18n::setLocale($this->fetchTable('Languages')->get($this->logged_user->language_id)->language_code);
$this->set('locale', I18n::getLocale());
/*
* Enable the following component for recommended CakePHP form protection settings.
* see https://book.cakephp.org/4/en/controllers/components/form-protection.html
*/
//$this->loadComponent('FormProtection');
}
public function beforeRender(EventInterface $event)
{
if ($this->getRequest()->is(['json', 'xml', 'csv'])) {
$this->viewBuilder()->disableAutoLayout();
}
$sys_admin_menu_items = null;
$menu_sections = [];
$menu_items = [];
$logged_user_id = null;
$logged_user_description = null;
$logged_user_access_type_description = null;
$logged_user_timezone = null;
$logged_user_organisation_description = null;
$logged_user_photo = Configure::read('App.defaultUserPhoto');
$user_notifications_count = 0;
$user_notifications = [];
// bisogna recuperare le capabilities utente (se esiste un utente loggato)
if ($this->logged_user) {
// menu SYSADMIN da mostrare solo ai SYSADMIN:
$sys_admin_menu_items = [
[
'title' => __('SYSADMIN'),
'icon' => 'lock',
'menu_items' => [
[
'title' => __('Gestione competenze'),
'icon' => 'wrench',
'link' => '/capabilities',
],
[
'title' => __('Test notifiche'),
'icon' => 'bell',
'link' => '/notifications/testAll',
'confirm' => __('Procedendo verranno inviate notifiche a tutti gli utenti e le organizzaioni che possiedono uno o più recapiti abilitati alle notifiche! Sei sicuro che vuoi continuare ?'),
'method' => 'POST',
],
],
],
];
if (!$this->logged_user->sys_admin) $sys_admin_menu_items = null;
$logged_user_id = $this->logged_user->id;
$logged_user_description = $this->logged_user->name . ' ' . $this->logged_user->surname;
$logged_user_access_type_description = $this->logged_user->sys_admin ? 'sys_admin' : ($this->logged_user->passepartout ? 'passepartout' : '');
$logged_user_timezone = $this->logged_user->timezone ?? Configure::read('App.defaultUserTimezone');
if (isset($this->logged_user->organisation_id)) {
$logged_user_organisation_description = $this->fetchTable('Organisations')->get($this->logged_user->organisation_id, contain: ['Actors'])->actor->description;
}
if (!empty($this->logged_user->photo)) $logged_user_photo = Configure::read('App.userPhotoSrc') . "/" . $this->logged_user->photo;
$user_notifications_count = $this->fetchTable('Notifications')->find()->where(['Notifications.user_id' => $this->logged_user->id, 'Notifications.read_by_user IS NULL'])->count();
$user_notifications = $this->fetchTable('Notifications')
->find()
->contain('NotificationTypes')
->where(['Notifications.user_id' => $this->logged_user->id, 'Notifications.read_by_user IS NULL'])
->order(['Notifications.created' => 'DESC'])
->all()
->reduce(function ($acc, $user_notification) {
$notification_info = json_decode($user_notification->info);
$body = strlen($notification_info->body) > 200 ? substr($notification_info->body, 0, 197) . '...' : $notification_info->body;
$icon = $user_notification->notification_type->icon ?? 'bell';
$time_ago = (new DateTime($user_notification->created))->timeAgoInWords();
$acc[] = ['id' => $user_notification->id, 'icon' => $icon, 'title' => $notification_info->title, 'body' => $body, 'time_ago' => $time_ago];
return $acc;
}, []);
$user_capabilityIds_query = $this->logged_user->getCapabilityIds(true);
$menu_sections_subquery = $this->fetchTable('MenuSections')
->find()
->select(['MenuSections.id'])
->innerJoinWith('Capabilities')
->where(['Capabilities.id IN' => $user_capabilityIds_query])
->distinct(['MenuSections.id']);
$menu_sections = $this->fetchTable('MenuSections')
->find()
->where(['MenuSections.id IN' => $menu_sections_subquery])
->order(['MenuSections.id' => 'ASC'])
->toArray();
if (count($menu_sections) > 0) {
// questo blocco controlla se nella request è presente il query param 'menu_section'
// se è presente e rappresenta un id di menu_section valido, lo settiamo in sessione utente
if ($this->getRequest()->getQuery('menu_section')) {
$valid_user_menu_section = $this->fetchTable('MenuSections')->find()
->matching('Capabilities')
->where(['Capabilities.id IN' => $user_capabilityIds_query, 'MenuSections.id' => $this->getRequest()->getQuery('menu_section')])
->first();
$valid_user_menu_section_id = $valid_user_menu_section ? $valid_user_menu_section->id : $menu_sections[0]->id;
$this->getRequest()->getSession()->write('menu_section', $valid_user_menu_section_id);
}
$menu_section_id = $this->getRequest()->getSession()->read('menu_section') ?? $menu_sections[0]->id;
$menu_items_subquery = $this->fetchTable('MenuItems')
->find()
->select(['MenuItems.id'])
->innerJoinWith('Capabilities')
->where(['Capabilities.id IN' => $user_capabilityIds_query])
->where(['MenuItems.menu_item_id IS NULL'])
->where(['MenuItems.menu_section_id' => $menu_section_id])
->distinct(['MenuItems.id']);
$menu_items = $this->fetchTable('MenuItems')
->find()
->where(['MenuItems.id IN' => $menu_items_subquery])
->order(['MenuItems.menu_order' => 'ASC'])
->toArray();
}
if( $this->request->getPath() !== '/notifications/index' )
{
$privacy = $this->fetchTable('Privacy')
->find()->orderBy(['created' => 'DESC'])
->first();
if(isset($privacy))
{
$hasAcceptedPrivacy = $this->fetchTable('PrivacyUsers')->find()->where([
'PrivacyUsers.user_id' => $this->logged_user->id,
'PrivacyUsers.privacy_id' => $privacy->id
])->first();
if(
!isset($hasAcceptedPrivacy) &&
$this->request->getParam('controller') != 'Privacy' &&
$this->request->getParam('action') != 'accept_privacy'
) {
return $this->redirect(['controller' => 'Privacy', 'action' => 'accept_privacy']);
}
}
}
}
$this->set('menu_sections', $menu_sections);
$this->set('menu_items', $menu_items);
$this->set('sys_admin_menu_items', $sys_admin_menu_items);
$this->set('logged_user_id', $logged_user_id);
$this->set('logged_user_description', $logged_user_description);
$this->set('logged_user_access_type_description', $logged_user_access_type_description);
$this->set('logged_user_timezone', $logged_user_timezone);
$this->set('logged_user_organisation_description', $logged_user_organisation_description);
$this->set('logged_user_photo', $logged_user_photo);
$this->set('user_notifications_count', $user_notifications_count);
$this->set('user_notifications', $user_notifications);
}
/**
* gen_random_code
*
* @param Int $length
* @return String
*/
protected function gen_random_code(Int $length): String {
$characters = "abcdefghijklmnopqrstuvwxyzABCDERFGHIJKLMNOPQRSTUVWXYZ0123456789";
$randomString = "";
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[mt_rand(0, strlen($characters)-1)];
}
return $randomString;
}
/**
* sendNotifications
*
* @param Int|Null $co_id
* @param String $notification_code
* @param Array $options
* @return void
*/
protected function sendNotifications(Int|Null $co_id, String $notification_code, Array $options)
{
NotificationsHandler::dispatch($co_id, $notification_code, $options, null, $this->logged_user->id ?? null);
}
/**
* applyFilters
*
* @param Query $query
* @return Query
*/
protected function applyFilters(Query $query): Query
{
// recuperiamo i parametri per costruire la chiave per memorizzare i filtri in sessione.
// i filtri sono "scoped" per utente e per route. Quindi per ogni utente possiamo memorizzare
// filtri relativi a più liste.
$logged_user_id = $this->logged_user->id;
$controller = $this->request->getParam('controller');
$action = $this->request->getParam('action');
$filter_session_key = "filters-$logged_user_id-$controller-$action";
// recuperiamo i filtri o dal POST data (significa che sono stati appena applicati)
// o dalla sessione (se ci sono):
$filters = $this->request->is('post') ? $this->request->getData('filters') : $this->request->getSession()->read($filter_session_key);
\Cake\Log\Log::write("debug", 'applyFilters - ' . ($this->request->is('post') ? 'POST received' : 'SESSION retrieved') . ' filters: ' . json_encode($filters));
// ripuliamo i filtri eliminando prima i valori non validi per ciascun filtro
// e successivamente eliminiamo direttamente i fitri che non hanno valori validi (rimasti):
$filters = collection($filters ?? [])
->map(function ($filter_values, $filter_id) {
return collection($filter_values)
->filter(function ($filter_value) {
// il controllo con === "0" serve per evitare che il sanitiser elimini
// il valore "0" per filtri di tipo "bool"
return !empty($filter_value) || $filter_value === "0";
})
->reduce(function ($filter_values, $filter_value) {
$filter_values[] = $filter_value;
return $filter_values;
}, []);
})
->filter(function ($filter_values, $filter_id) {
return !empty($filter_values);
})
->toArray();
\Cake\Log\Log::write("debug", 'applyFilters - SANITISED filters: ' . json_encode($filters));
// se non si è riusciti a recuperare filtri
// o è stato richesto un reset dei filtri tramite POST
// o non ne sono rimasti a seguito della sanitizzazione,
// cancelliamo comunque la sessione alla chiave $filter_session_key
// ed usciamo senza applicare nulla alla query:
if (empty($filters)) {
\Cake\Log\Log::write("debug", "applyFilters - DELETING DATA SESSION with key '$filter_session_key'");
$this->request->getSession()->delete($filter_session_key);
\Cake\Log\Log::write("debug", 'applyFilters - NO filters to appply!');
return $query;
}
// se abbiamo filtri validi e li abbiamo appena ricevuti (POST), li salviamo in sessione:
if ($this->request->is('post')) {
\Cake\Log\Log::write("debug", "applyFilters - WRITING DATA SESSION with key '$filter_session_key': " . json_encode($filters));
$this->request->getSession()->write($filter_session_key, $filters);
}
// array delle possibili associations da gestire nella query con ->leftJoinWith().
// verrà popolato durante il processo di conversione da filtri a conditions:
$associations = [];
// array delle conditions creato a partire dai filters ricevuti, da gestire nella query con ->where()
$conditions = collection($filters)
// qui trasformiamo i filtri in conditions in base alla tipologia di filtro ricevuto:
->map(function ($filter_values, $filter_id) use (&$associations) {
// recuperiamo le info sul filtro dalla table 'filters' avendo ricevuto l'id del filtro:
$filter = $this->fetchTable('Filters')->get((Int)explode("filter_id_", $filter_id)[1]);
// creazione della condizione in base al tipo di filtro:
switch ($filter->filter_type) {
case 'class':
case 'bool':
$condition = [$filter->search . ' IN' => $filter_values];
break;
case 'text':
$condition = collection($filter_values)
->map(function ($filter_value) use ($filter) {
return [$filter->search . ' LIKE' => $filter_value];
})
->reduce(function ($conditions, $condition) {
$conditions['OR'][] = $condition;
return $conditions;
}, ['OR' => []]);
break;
case 'date':
$condition = collection($filter_values)
->map(function ($filter_value) use ($filter) {
$date_array = explode(" - ", $filter_value);
$from_date = $date_array[0];
$to_date = $date_array[1];
return [$filter->search . ' >=' => $from_date, $filter->search . ' <=' => $to_date];
})
->reduce(function ($conditions, $condition) {
$conditions['OR'][] = $condition;
return $conditions;
}, ['OR' => []]);
break;
default:
break;
}
// se il filtro è stato gestito e richiede una association, la inseriamo in apposito array:
if (!empty($condition) && !empty($filter->associations)) $associations[] = $filter->associations;
// se il filtro non è stato gestito, restituiamo null; altrimenti restituiamo la condition
return $condition ?? null;
})
// qui eliminiamo eventuali conditions null (generate da filtri non (al momento) gestiti:
->filter(function ($condition) {
return !empty($condition);
})
// infine restituiamo l'array_values delle conditions ottenute:
->reduce(function ($conditions, $condition) {
$conditions[] = $condition;
return $conditions;
}, []);
\Cake\Log\Log::write("debug", 'applyFilters - conditions applied: ' . json_encode($conditions));
if (count($conditions) > 0) $query = $query->where($conditions);
\Cake\Log\Log::write("debug", 'applyFilters - associations applied: ' . json_encode($associations));
foreach ($associations as $association) {
$query = $query->leftJoinWith($association);
}
if (count($associations) > 0) {
$main_table = $query->getRepository()->getAlias();
$query = $query->distinct(["$main_table.id"]);
\Cake\Log\Log::write("debug", 'applyFilters - distinct applied: ' . "$main_table.id");
}
return $query;
}
/**
* Respond with JSON
*
* @param int $statusCode HTTP status code
* @param null|object|array|string|null $responseBody Content of the response
* @param string $contentType Content type of the response, default "application/json"
* @return Response
*/
public function respond(int $statusCode = 200, null|object|array|string $responseBody = null, string $contentType = "application/json") : Response
{
$responseBody = is_object($responseBody) || is_array($responseBody) ? $responseBody : ['result' => $responseBody];
if ($paging = $this->request->getAttribute('paging')) {
$responseBody['pagination'] = $paging;
}
try {
$encodedBody = json_encode($responseBody, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
return $this->getResponse()
->withStatus(500)
->withStringBody(json_encode(['error' => 'Failed to encode JSON response']))
->withType('application/json');
}
$response = $this->getResponse()
->withStatus($statusCode)
->withType($contentType)
->withStringBody($encodedBody);
return $response;
}
}

112
idrocap_wa/src/Controller/ApplicantsController.php

@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
use Cake\View\JsonView;
/**
* Applicants Controller
*
* @property \App\Model\Table\ApplicantsTable $Applicants
* @method \App\Model\Entity\Applicant[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class ApplicantsController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class];
}
public function search_applicants(){
$this->viewBuilder()->setClassName('Json');
$search = $this->request->getQueryParams('term');
$applicants = $this->Applicants->find()->where([
'Applicants.tax_code LIKE "%' . $search['term'] . '%" OR Applicants.name LIKE "%' . $search['term'] . '%" OR Applicants.surname LIKE "%' . $search['term'] . '%"'
])->formatResults(function ($results){
return $results->map(function ($row) {
return ['id' => $row->id, 'text' => $row->tax_code . ' - ' . $row->name . ' ' . $row->surname];
});
})->toArray();
$this->set(compact('applicants'));
$this->viewBuilder()->setOption('serialize', ['applicants'])->setOption('jsonOptions', JSON_FORCE_OBJECT);
}
public function get_applicant($id){
$applicant = $this->Applicants->get($id);
$this->set(compact('applicant'));
$this->viewBuilder()->setOption('serialize', ['applicant'])->setOption('jsonOptions', JSON_FORCE_OBJECT);
}
public function edit($id, $water_id = null){
$applicant = $this->Applicants->get($id);
if ($this->request->is(['patch', 'post', 'put'])) {
$applicant = $this->Applicants->patchEntity($applicant, $this->request->getData());
if ($this->Applicants->save($applicant)) {
$this->Flash->success(__('Il concessionario è stato aggiornato correttamente.'));
return $this->redirect(['action' => 'view', $id]);
}
$this->Flash->error(__('Errore. Il concessionario non è stato salvato.'));
}
if(isset($water_id)){
$waterDrawingPaperwork = $this->Applicants->WaterDrawingPaperworks->get($water_id);
$this->set(compact('waterDrawingPaperwork'));
}
$this->set(compact('applicant'));
}
public function view($id){
$applicant = $this->Applicants->get($id);
$this->set(compact('applicant'));
}
// CITIZEN SECTION
public function citizen_add(){
if (!$this->logged_user->hasCapability(['documentation.water_drawing_paperworks.citizen'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$applicant = $this->Applicants->newEmptyEntity();
if ($this->request->is(['patch', 'post', 'put'])) {
$data = $this->request->getData();
$data['name'] = $this->logged_user->name;
$data['surname'] = $this->logged_user->surname;
$data['tax_code'] = $this->logged_user->tax_code;
$applicant = $this->Applicants->patchEntity($applicant, $data);
if ($this->Applicants->save($applicant)) {
$this->Flash->success(__('Il concessionario è stato creato correttamente.'));
return $this->redirect(['action' => 'citizen_view']);
}
$this->Flash->error(__('Errore. Il concessionario non è stato salvato.'));
}
$user = $this->getTableLocator()->get('Users')->get($this->logged_user->id);
$this->set('user', $user);
$this->set(compact('applicant'));
}
public function citizen_edit(){
if (!$this->logged_user->hasCapability(['documentation.water_drawing_paperworks.citizen'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$user = $this->getTableLocator()->get('Users')->get($this->logged_user->id);
$applicant = $this->Applicants->find()->where(['Applicants.tax_code' => $user->tax_code])->first();
if ($this->request->is(['patch', 'post', 'put'])) {
$applicant = $this->Applicants->patchEntity($applicant, $this->request->getData());
if ($this->Applicants->save($applicant)) {
$this->Flash->success(__('Il concessionario è stato aggiornato correttamente.'));
return $this->redirect(['action' => 'citizen_view']);
}
$this->Flash->error(__('Errore. Il concessionario non è stato salvato.'));
}
$this->set('user', $user);
$this->set(compact('applicant'));
}
public function citizen_view(){
if (!$this->logged_user->hasCapability(['documentation.water_drawing_paperworks.citizen'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$user = $this->getTableLocator()->get('Users')->get($this->logged_user->id);
$applicant = $this->Applicants->find()->where(['Applicants.tax_code' => $user->tax_code])->first();
if(!isset($applicant)) $this->redirect(['action' => 'citizen_add']);
$this->set(compact('applicant'));
}
}

98
idrocap_wa/src/Controller/AttachmentsController.php

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Core\Configure;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\ForbiddenException;
use Cake\View\JsonView;
/**
* Attachments Controller
*
* @property \App\Model\Table\AttachmentsTable $Attachments
* @method \App\Model\Entity\Attachment[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class AttachmentsController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class];
}
/**
* view
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function view($id)
{
$withTrashed = $this->request->getQuery('withTrashed');
$finder = !empty($withTrashed) ? 'withTrashed' : 'all';
$attachment = $this->fetchTable('Attachments')->find($finder)
->contain(['ContainerControllableObjects'])
->where(['file_name' => $id])
->first();
if (!$attachment) throw new NotFoundException(__('Allegato non trovato'));
if (!$this->logged_user->canHandleAttachmentsForControllableObject($attachment->container_controllable_object)) throw new ForbiddenException(__('Non hai i permessi necessari per visionare l\'allegato'));
$mimetype = $attachment->mimetype;
$file_name = $attachment->file_name;
$original_file_name = $attachment->original_file_name;
$container_co_id = $attachment->container_controllable_object_id;
$path = Configure::read('App.attachmentsPath') . "/" . $container_co_id . "/" . $file_name;
$output = \App\WGS\FileStorage\FileStorageFactory::create()->getFile($path);
$this->response = $this->response
->withAddedHeader('Content-Disposition', 'filename="' . $original_file_name . '"')
->withCharset('UTF-8')
->withType($mimetype)
->withStringBody($output);
return $this->response;
}
/**
* markAsRelevant
*
* @param Int $id
* @throws NotFoundException
* @return \Cake\Http\Response
*/
public function markAsRelevant($id = null)
{
$this->request->allowMethod(['post']);
$attachment = $this->fetchTable('Attachments')->find()->contain(['ContainerControllableObjects', 'ControllableObjects'])->where(['file_name' => $id])->first();
if(!$attachment) throw new NotFoundException(__('Allegato non trovato'));
if (!$this->logged_user->canHandleAttachmentsForControllableObject($attachment->container_controllable_object)) throw new ForbiddenException(__('Non hai i permessi necessari per visionare l\'allegato'));
$attachment->relevant = !$attachment->relevant;
$attachment->controllable_object->id = $attachment->controllable_object->id;
$attachment->setDirty('controllable_object');
$this->fetchTable('Attachments')->save($attachment);
$response = (object) [];
$response->result = true;
$response->relevant = $attachment->relevant;
$this->set('response', $response);
$this->viewBuilder()->setOption('serialize', 'response');
}
/**
* markAsDeleted
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function markAsDeleted($id = null)
{
$this->response = $this->response
->withType('application/json')
->withStringBody(json_encode(['result' => "markAsDeleted attachment with id '$id'. Dummy method. please do not delete it!"]));
return $this->response;
}
}

205
idrocap_wa/src/Controller/CapabilitiesController.php

@ -0,0 +1,205 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
use Cake\Datasource\ConnectionManager;
/**
* Capabilities Controller
*
* @property \App\Model\Table\CapabilitiesTable $Capabilities
* @method \App\Model\Entity\Capability[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class CapabilitiesController extends AppController
{
/**
* index
*
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function index()
{
if (!$this->logged_user->sys_admin) throw new ForbiddenException(__('Non hai i permessi necessari'));
$capabilities = $this->Capabilities->find('withTrashed')
->contain(['CapabilityGroups' => function ($q) { return $q->find('withTrashed'); }]);
$capability_search = $this->request->getQuery('capability_search');
if (!empty($capability_search)) {
$capabilities = $capabilities->where(['OR' => [
'Capabilities.description LIKE' => "%$capability_search%",
'Capabilities.value LIKE' => "%$capability_search%",
'CapabilityGroups.description LIKE' => "%$capability_search%",
]]);
}
$this->paginate = [
'sortableFields' => ['description', 'value', 'priority', 'CapabilityGroups.description'],
];
$this->set('capabilities', $this->paginate($capabilities));
$this->set('capability_search', $capability_search);
}
/**
* hide
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function hide($id)
{
if (!$this->logged_user->sys_admin) throw new ForbiddenException(__('Non hai i permessi necessari'));
$capability = $this->Capabilities->find('withTrashed')->where(['Capabilities.id' => $id])->first();
$connection = ConnectionManager::get('default');
$errSave = true;
$connection->begin();
$capability->is_configurable = false;
if ($this->Capabilities->save($capability)) {
$capability_group = $this->Capabilities->CapabilityGroups->find('withTrashed')
->contain(['Capabilities' => function ($q) { return $q->find('withTrashed')->where(['Capabilities.is_configurable' => 1]); }])
->where(['CapabilityGroups.id' => $capability->capability_group_id])
->first();
if ($capability_group) {
if (count($capability_group->capabilities) == 0) {
$capability_group->is_configurable = false;
if ($this->Capabilities->CapabilityGroups->save($capability_group)) {
$errSave = false;
}
} else {
$errSave = false;
}
}
}
if (!$errSave) {
$this->Flash->success(__('Competenza "{0}" nascosta con successo!', $capability->description));
$connection->commit();
} else {
$this->Flash->error(__('Errore durante il tentativo di nascondere la Competenza "{0}"!', $capability->description));
$connection->rollback();
}
return $this->redirect(['action' => 'index', '?' => $this->request->getQueryParams()]);
}
/**
* show
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function show($id)
{
if (!$this->logged_user->sys_admin) throw new ForbiddenException(__('Non hai i permessi necessari'));
$capability = $this->Capabilities->find('withTrashed')->where(['Capabilities.id' => $id])->first();
$connection = ConnectionManager::get('default');
$errSave = true;
$connection->begin();
$capability->is_configurable = true;
if ($this->Capabilities->save($capability)) {
$capability_group = $this->Capabilities->CapabilityGroups->find('withTrashed')
->where(['CapabilityGroups.id' => $capability->capability_group_id])
->first();
if ($capability_group) {
$capability_group->is_configurable = true;
if ($this->Capabilities->CapabilityGroups->save($capability_group)) {
$errSave = false;
}
}
}
if (!$errSave) {
$this->Flash->success(__('Competenza "{0}" resa visibile con successo!', $capability->description));
$connection->commit();
} else {
$this->Flash->error(__('Errore durante il tentativo di rendere visibile la Competenza "{0}"!', $capability->description));
$connection->rollback();
}
return $this->redirect(['action' => 'index', '?' => $this->request->getQueryParams()]);
}
/**
* delete
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function delete($id)
{
if (!$this->logged_user->sys_admin) throw new ForbiddenException(__('Non hai i permessi necessari'));
$this->Capabilities->Permissions->addBehavior('Muffin/Trash.Trash');
$capability = $this->Capabilities->find('withTrashed')->where(['Capabilities.id' => $id])->first();
$connection = ConnectionManager::get('default');
$errSave = true;
$connection->begin();
if ($this->Capabilities->delete($capability)) {
$capability_group = $this->Capabilities->CapabilityGroups->find('withTrashed')
->contain(['Capabilities'])
->where(['CapabilityGroups.id' => $capability->capability_group_id])
->first();
if ($capability_group) {
if (count($capability_group->capabilities) == 0) {
if ($this->Capabilities->CapabilityGroups->delete($capability_group)) {
$errSave = false;
}
} else {
$errSave = false;
}
}
}
if (!$errSave) {
$this->Flash->success(__('Competenza "{0}" cancellata con successo!', $capability->description));
$connection->commit();
} else {
$this->Flash->error(__('Errore durante la cancellazione della Competenza "{0}"!', $capability->description));
$connection->rollback();
}
return $this->redirect(['action' => 'index', '?' => $this->request->getQueryParams()]);
}
/**
* restore
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function restore($id)
{
if (!$this->logged_user->sys_admin) throw new ForbiddenException(__('Non hai i permessi necessari'));
$this->Capabilities->Permissions->addBehavior('Muffin/Trash.Trash');
$capability = $this->Capabilities->find('withTrashed')->where(['Capabilities.id' => $id])->first();
$connection = ConnectionManager::get('default');
$errSave = true;
$connection->begin();
if ($this->Capabilities->cascadingRestoreTrash($capability)) {
$capability_group = $this->Capabilities->CapabilityGroups->find('withTrashed')
->where(['CapabilityGroups.id' => $capability->capability_group_id])
->first();
if ($capability_group) {
$capability_group->deleted = null;
if ($this->Capabilities->CapabilityGroups->save($capability_group)) {
$errSave = false;
}
}
}
if (!$errSave) {
$this->Flash->success(__('Competenza "{0}" ripristinata con successo!', $capability->description));
$connection->commit();
} else {
$this->Flash->error(__('Errore durante il ripristino della Competenza "{0}"!', $capability->description));
$connection->rollback();
}
return $this->redirect(['action' => 'index', '?' => $this->request->getQueryParams()]);
}
}

0
idrocap_wa/src/Controller/Component/.gitkeep

103
idrocap_wa/src/Controller/Component/StreamableCsvExportTrait.php

@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
namespace App\Controller\Component;
use Cake\Http\CallbackStream;
use Cake\ORM\Query\SelectQuery;
use Cake\Http\Response;
trait StreamableCsvExportTrait
{
/**
* Stream a CSV file to the browser fetching data in configurable chunk size.
*
* @param SelectQuery $query Query returning the dataset
* @param string $filename Name of the file to download
* @param array|null $extract Optional: extraction map to apply
* @param array|null $headers Optional: column keys to export (and order). $headers are taken in account ONLY when $extract is provided!
* @param array $csvOptions CSV formatting options: delimiter, enclosure, escape
* @param int $chunkSize Number of rows per DB page
* @param bool $bom whether CSV should contain UTF-8 BOM (Byte Order Mark) at the very beginning of file (for Excel compatibilty)
* @return Response
*/
public function streamCsvResponse(
SelectQuery $query,
string $filename = 'export.csv',
?array $extract = null,
?array $headers = null,
array $csvOptions = [',', '"', '\\'],
int $chunkSize = 1000,
bool $bom = true
): Response {
$stream = new CallbackStream(function () use ($query, $extract, $headers, $csvOptions, $chunkSize, $bom) {
$out = fopen('php://output', 'w');
// Excel-friendly UTF-8 BOM
if ($bom) fwrite($out, "\xEF\xBB\xBF");
$page = 1;
$wroteHeader = false;
// we disable CakePHP ORM istantiating Model Entities
$query->disableHydration();
do {
$results = $query
->limit($chunkSize)
->page($page)
->all();
foreach ($results as $rowArray) {
if ($extract !== null) {
if (!$wroteHeader) {
// we use the provided $headers as column names,
// otherwise we fallback to $extract array keys when they are plain string or we use "Column $key" if they are not
$headerRow = $headers ?? array_map(
function($extract_value, $extract_index) { return is_string($extract_value) ? $extract_value : "Column $extract_index"; },
$extract,
array_keys($extract)
);
fputcsv($out, $headerRow, ...$csvOptions);
$wroteHeader = true;
}
$dataRow = [];
// we use the provided $extractor to obtain the mapped values:
foreach ($extract as $column) {
if (is_callable($column)) {
$dataRow[] = $column($rowArray);
} elseif (is_string($column)) {
$dataRow[] = $rowArray[$column] ?? '';
} else {
$dataRow[] = '';
}
}
fputcsv($out, $dataRow, ...$csvOptions);
} else {
// there is no exctractor provided, we will take plain array keys as column names:
if (!$wroteHeader) {
fputcsv($out, array_keys($rowArray), ...$csvOptions);
$wroteHeader = true;
}
// there is no exctractor provided, we will take plain array values:
$rowValues = array_values($rowArray);
fputcsv($out, $rowValues, ...$csvOptions);
}
flush();
}
$count = $results->count();
$page++;
} while ($count === $chunkSize);
fclose($out);
});
return $this->response
->withType('text/csv')
->withDownload($filename)
->withBody($stream);
}
}

56
idrocap_wa/src/Controller/ControllableObjectsController.php

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace App\Controller;
/**
* ControllableObjects Controller
*
* @property \App\Model\Table\ControllableObjectsTable $ControllableObjects
* @method \App\Model\Entity\ControllableObject[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class ControllableObjectsController extends AppController
{
/**
* getAttachments
*
* @param Int $co_id
* @return \Cake\Http\Response
*/
public function getAttachments ($co_id = null) {
$attachments = $this->ControllableObjects->Attachments
->find()
->contain(['ControllableObjects' => ['Creator', 'Modifier', 'Locations']])
->where(['Attachments.container_controllable_object_id' => $co_id]);
if(!empty($this->request->getQuery('tags'))) {
$tags = explode(',', $this->request->getQuery('tags'));
$attachments = $attachments
->matching('Tags')
->where(['Tags.code IN' => $tags]);
}
$showPrivateColumn = $attachments->all()->filter(function ($attachment) { return $attachment->private == 1; })->count() > 0;
if (!$this->logged_user->canHandleAttachmentsForControllableObject($this->ControllableObjects->get($co_id))) {
$attachments = $attachments->where(['Attachments.id < 0']);
}
$this->paginate = [
'sortableFields' => ['Attachments.private', 'Attachments.original_file_name', 'Locations.description', 'Attachments.relevant', 'Attachments.original_file_size', 'Attachments.mimetype', 'Attachments.upload_date', 'Creator.surname'],
'limit' => 10,
'order' => [
'Attachments.upload_date' => 'DESC'
],
];
if ($this->request->getQuery('removecurrentfiles') !== null) {
$this->set('removecurrentfiles', $this->request->getQuery('removecurrentfiles'));
}
$this->viewBuilder()->setLayout('ajax');
$this->set('numberOfAttachments', $attachments->count());
$this->set('attachments', $this->paginate($attachments));
$this->set('showPrivateColumn', $showPrivateColumn);
}
}

35
idrocap_wa/src/Controller/DashboardController.php

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.2.9
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller;
/**
* Static content controller
*
* This controller will render views from templates/Dashboard/
*
*/
class DashboardController extends AppController
{
/**
* index
*
* @return \Cake\Http\Response
*/
public function index ()
{
}
}

185
idrocap_wa/src/Controller/DeliveriesController.php

@ -0,0 +1,185 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
/**
* Deliveries Controller
*
* @property \App\Model\Table\DeliveriesTable $Deliveries
* @method \App\Model\Entity\Delivery[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class DeliveriesController extends AppController
{
/**
* index
*
* @param Int $actor_id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function index($actor_id)
{
$actor = $this->Deliveries->Actors->get($actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canViewContactsForActor($actor)) throw new ForbiddenException(__('Non hai i permessi per visualizzare i recapiti per {0}', $actor_type_description));
$mobile_phones = $this->Deliveries->MobilePhones->find()
->contain(['MobilePhoneDescriptions'])
->matching('Deliveries')
->where(['Deliveries.actor_id' => $actor_id])
->all();
if (!$this->logged_user->canViewContactsForActor($actor, 'mobile_phone')) {
$mobile_phones = $mobile_phones->map(function ($mobile_phone) {
$mobile_phone->value = "**************";
return $mobile_phone;
});
}
$faxes = $this->Deliveries->Faxes->find()
->contain(['FaxDescriptions'])
->matching('Deliveries')
->where(['Deliveries.actor_id' => $actor_id])
->all();
if (!$this->logged_user->canViewContactsForActor($actor, 'fax')) {
$faxes = $faxes->map(function ($fax) {
$fax->value = "**************";
return $fax;
});
}
$emails = $this->Deliveries->Emails->find()
->contain(['EmailDescriptions'])
->matching('Deliveries')
->where(['Deliveries.actor_id' => $actor_id])
->all();
if (!$this->logged_user->canViewContactsForActor($actor, 'email')) {
$emails = $emails->map(function ($email) {
$email->value = "**************";
return $email;
});
}
$phones = $this->Deliveries->Phones->find()
->contain(['PhoneDescriptions'])
->matching('Deliveries')
->where(['Deliveries.actor_id' => $actor_id])
->all();
if (!$this->logged_user->canViewContactsForActor($actor, 'phone')) {
$phones = $phones->map(function ($phone) {
$phone->value = "**************";
return $phone;
});
}
$telegram_chats = $this->Deliveries->TelegramChats->find()
->matching('Deliveries')
->where(['Deliveries.actor_id' => $actor_id])
->all();
if (!$this->logged_user->canViewContactsForActor($actor, 'telegram_chat')) {
$telegram_chats = $telegram_chats->map(function ($telegram_chat) {
$telegram_chat->value = "**************";
return $telegram_chat;
});
}
$pecs = $this->Deliveries->Pecs->find()
->contain(['PecDescriptions'])
->matching('Deliveries')
->where(['Deliveries.actor_id' => $actor_id])
->all();
if (!$this->logged_user->canViewContactsForActor($actor, 'pec')) {
$pecs = $pecs->map(function ($pec) {
$pec->value = "**************";
return $pec;
});
}
$can_add_mobile_phone = $this->logged_user->canAddContactsForActor($actor, 'mobile_phone');
$can_edit_mobile_phone = $this->logged_user->canEditContactsForActor($actor, 'mobile_phone');
$can_delete_mobile_phone = $this->logged_user->canDeleteContactsForActor($actor, 'mobile_phone');
$can_add_fax = $this->logged_user->canAddContactsForActor($actor, 'fax');
$can_edit_fax = $this->logged_user->canEditContactsForActor($actor, 'fax');
$can_delete_fax = $this->logged_user->canDeleteContactsForActor($actor, 'fax');
$can_add_email = $this->logged_user->canAddContactsForActor($actor, 'email');
$can_edit_email = $this->logged_user->canEditContactsForActor($actor, 'email');
$can_delete_email = $this->logged_user->canDeleteContactsForActor($actor, 'email');
$can_add_phone = $this->logged_user->canAddContactsForActor($actor, 'phone');
$can_edit_phone = $this->logged_user->canEditContactsForActor($actor, 'phone');
$can_delete_phone = $this->logged_user->canDeleteContactsForActor($actor, 'phone');
$can_add_telegram_chat = $this->logged_user->canAddContactsForActor($actor, 'telegram_chat');
$can_edit_telegram_chat = $this->logged_user->canEditContactsForActor($actor, 'telegram_chat');
$can_delete_telegram_chat = $this->logged_user->canDeleteContactsForActor($actor, 'telegram_chat');
$can_add_pec = $this->logged_user->canAddContactsForActor($actor, 'pec');
$can_edit_pec = $this->logged_user->canEditContactsForActor($actor, 'pec');
$can_delete_pec = $this->logged_user->canDeleteContactsForActor($actor, 'pec');
$this->set(compact('actor', 'mobile_phones', 'faxes', 'emails', 'phones', 'telegram_chats', 'pecs', 'can_add_mobile_phone', 'can_edit_mobile_phone', 'can_delete_mobile_phone', 'can_add_fax', 'can_edit_fax', 'can_delete_fax', 'can_add_email', 'can_edit_email', 'can_delete_email', 'can_add_phone', 'can_edit_phone', 'can_delete_phone', 'can_add_telegram_chat', 'can_edit_telegram_chat', 'can_delete_telegram_chat', 'can_add_pec', 'can_edit_pec', 'can_delete_pec'));
}
/**
* delete
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function delete($id = null)
{
$this->request->allowMethod(['post', 'delete']);
$delivery = $this->Deliveries->get($id, contain: ['DeliveryTypes', 'MobilePhones' => ['MobilePhoneDescriptions'], 'Faxes' => ['FaxDescriptions'], 'Emails' => ['EmailDescriptions'], 'Phones' => ['PhoneDescriptions'], 'TelegramChats', 'Pecs' => ['PecDescriptions']]);
$actor = $this->Deliveries->Actors->get($delivery->actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canDeleteContactsForActor($actor, $delivery->delivery_type->description)) throw new ForbiddenException(__('Non hai i permessi per eliminare i recapiti di questo tipo per {0}', $actor_type_description));
$contact_value = "";
switch ($delivery->delivery_type_id) {
case 1:
$contact_value = __('Cellulare ({0}): {1}', $delivery->mobile_phone->mobile_phone_description->description, $this->logged_user->canViewContactsForActor($actor, $delivery->delivery_type->description) ? $delivery->mobile_phone->value : "**************");
break;
case 2:
$contact_value = __('Fax ({0}): {1}', $delivery->fax->fax_description->description, $this->logged_user->canViewContactsForActor($actor, $delivery->delivery_type->description) ? $delivery->fax->value: "**************");
break;
case 3:
$contact_value = __('Email ({0}): {1}', $delivery->email->email_description->description, $this->logged_user->canViewContactsForActor($actor, $delivery->delivery_type->description) ? $delivery->email->value: "**************");
break;
case 4:
$contact_value = __('Telefono ({0}): {1}', $delivery->phone->phone_description->description, $this->logged_user->canViewContactsForActor($actor, $delivery->delivery_type->description) ? $delivery->phone->value: "**************");
break;
case 5:
$contact_value = __('Telegram Chat ID: {0}', $this->logged_user->canViewContactsForActor($actor, $delivery->delivery_type->description) ? $delivery->telegram_chat->value: "**************");
break;
case 6:
$contact_value = __('PEC ({0}): {1}', $delivery->pec->pec_description->description, $this->logged_user->canViewContactsForActor($actor, $delivery->delivery_type->description) ? $delivery->pec->value: "**************");
break;
default:
$contact_value = "";
break;
}
if ($this->Deliveries->delete($delivery)) {
$this->Flash->success(__('Il recapito "{0}" è stato cancellato con successo', $contact_value));
} else {
$this->Flash->error(__('Errore durante la cancellazione del recapito {0}. Riprovare di nuovo.', $contact_value));
}
return $this->redirect(['action' => 'index', $delivery->actor_id]);
}
}

66
idrocap_wa/src/Controller/DistrictsController.php

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Model\Table\DistrictsTable;
use App\WGS\Utils\Enum\ProvinceDistrictFields;
use App\WGS\Utils\Enum\Regions;
use App\WGS\Utils\Helper\ProvinceDistrictFieldHelper;
use Cake\View\JsonView;
use Exception;
/**
* Applicants Controller
*
* @property DistrictsTable $Districts
* @method \App\Model\Entity\Applicant[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class DistrictsController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class];
}
/**
* @throws Exception
*/
public function search_districts()
{
$this->viewBuilder()->setClassName('Json');
$conditions = [];
$provinceCode = $this->request->getQuery(ProvinceDistrictFieldHelper::raw(ProvinceDistrictFields::PROVINCE_CODE));
$term = $this->request->getQuery("term");
$codeReg = ProvinceDistrictFieldHelper::getCodeRegByRegion(Regions::SICILY);
$regCodField = ProvinceDistrictFieldHelper::forDistricts(ProvinceDistrictFields::REGION_CODE);
$provCodeField = ProvinceDistrictFieldHelper::forDistricts(ProvinceDistrictFields::PROVINCE_CODE);
$queryDistrictFieldName = ProvinceDistrictFieldHelper::forDistricts(ProvinceDistrictFields::DISTRICT_FIELD_NAME);
if($codeReg) {
$conditions[$regCodField] = $codeReg;
}
if (isset($provinceCode)) {
$conditions[$provCodeField] = $provinceCode;
}
$query = $this->Districts->find();
if ($term) {
$query->where(function ($exp) use ($term, $queryDistrictFieldName) {
return $exp->like($queryDistrictFieldName, "%$term%");
});
}
if (!empty($conditions)) {
$query->where($conditions);
}
$district_field = ProvinceDistrictFieldHelper::raw(ProvinceDistrictFields::DISTRICT_FIELD_NAME);
$districts = $query->find('list', keyField: $district_field, valueField: $district_field)->toArray();
$this->set(compact('districts'));
$this->viewBuilder()->setOption('serialize', ['districts'])->setOption('jsonOptions', JSON_FORCE_OBJECT);
}
}

78
idrocap_wa/src/Controller/EmailsController.php

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
/**
* Emails Controller
*
* @property \App\Model\Table\EmailsTable $Emails
*/
class EmailsController extends AppController
{
/**
* add
*
* @param Int $actor_id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function add($actor_id)
{
$actor = $this->Emails->Deliveries->Actors->get($actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canAddContactsForActor($actor, 'email')) throw new ForbiddenException(__('Non hai i permessi per aggiungere i recapiti di questo tipo per {0}', $actor_type_description));
$email = $this->Emails->newEntity([
'delivery' => [
'actor_id' => $actor_id,
'delivery_type_id' => 3,
],
], ['validate' => false]);
if ($this->request->is('post')) {
$email = $this->Emails->patchEntity($email, $this->request->getData());
if ($this->Emails->save($email)) {
$this->Flash->success(__('L\'Email è stata aggiunta con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $actor_id]);
}
$this->Flash->error(__('Errore durante l\'aggiunta dell\'Email'));
}
$emailDescriptions = $this->Emails->EmailDescriptions->find('list');
$this->set(compact('actor', 'email', 'emailDescriptions'));
}
/**
* edit
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
$email = $this->Emails->get($id, contain: ['Deliveries' => ['Actors' => ['ActorTypes']]]);
$actor = $this->Emails->Deliveries->Actors->get($email->delivery->actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canEditContactsForActor($actor, 'email')) throw new ForbiddenException(__('Non hai i permessi per modificare i recapiti di questo tipo per {0}', $actor_type_description));
if ($this->request->is(['patch', 'post', 'put'])) {
$email = $this->Emails->patchEntity($email, $this->request->getData());
if ($this->Emails->save($email)) {
$this->Flash->success(__('L\'Email è stata modificata con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $email->delivery->actor_id]);
}
$this->Flash->error(__('Errore durante la modifica dell\'Email. Riprovare di nuovo'));
}
$emailDescriptions = $this->Emails->EmailDescriptions->find('list');
$this->set(compact('actor', 'email', 'emailDescriptions'));
}
}

70
idrocap_wa/src/Controller/ErrorController.php

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.3.4
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller;
use Cake\Event\EventInterface;
/**
* Error Handling Controller
*
* Controller used by ExceptionRenderer to render error responses.
*/
class ErrorController extends AppController
{
/**
* Initialization hook method.
*
* @return void
*/
public function initialize(): void
{
parent::initialize();
}
/**
* beforeFilter callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function beforeFilter(EventInterface $event)
{
}
/**
* beforeRender callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function beforeRender(EventInterface $event)
{
parent::beforeRender($event);
$this->viewBuilder()->setTemplatePath('Error');
}
/**
* afterFilter callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function afterFilter(EventInterface $event)
{
}
}

78
idrocap_wa/src/Controller/FaxesController.php

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
/**
* Faxes Controller
*
* @property \App\Model\Table\FaxesTable $Faxes
*/
class FaxesController extends AppController
{
/**
* add
*
* @param Int $actor_id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function add($actor_id)
{
$actor = $this->Faxes->Deliveries->Actors->get($actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canAddContactsForActor($actor, 'fax')) throw new ForbiddenException(__('Non hai i permessi per aggiungere i recapiti di questo tipo per {0}', $actor_type_description));
$fax = $this->Faxes->newEntity([
'delivery' => [
'actor_id' => $actor_id,
'delivery_type_id' => 2,
],
], ['validate' => false]);
if ($this->request->is('post')) {
$fax = $this->Faxes->patchEntity($fax, $this->request->getData());
if ($this->Faxes->save($fax)) {
$this->Flash->success(__('Il Fax è stato aggiunto con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $actor_id]);
}
$this->Flash->error(__('Errore durante l\'aggiunta del Fax'));
}
$faxDescriptions = $this->Faxes->FaxDescriptions->find('list');
$this->set(compact('actor', 'fax', 'faxDescriptions'));
}
/**
* edit
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
$fax = $this->Faxes->get($id, contain: ['Deliveries' => ['Actors' => ['ActorTypes']]]);
$actor = $this->Faxes->Deliveries->Actors->get($fax->delivery->actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canEditContactsForActor($actor, 'fax')) throw new ForbiddenException(__('Non hai i permessi per modificare i recapiti di questo tipo per {0}', $actor_type_description));
if ($this->request->is(['patch', 'post', 'put'])) {
$fax = $this->Faxes->patchEntity($fax, $this->request->getData());
if ($this->Faxes->save($fax)) {
$this->Flash->success(__('Il Fax è stato modificato con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $fax->delivery->actor_id]);
}
$this->Flash->error(__('Errore durante la modifica del Fax. Riprovare di nuovo'));
}
$faxDescriptions = $this->Faxes->FaxDescriptions->find('list');
$this->set(compact('actor', 'fax', 'faxDescriptions'));
}
}

25
idrocap_wa/src/Controller/FiltersController.php

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Controller;
/**
* Filters Controller
*
* @property \App\Model\Table\FiltersTable $Filters
* @method \App\Model\Entity\Filter[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class FiltersController extends AppController
{
/**
* getFilterInput
*
* @param Int $filter_id
* @return \Cake\Http\Response
*/
public function getFilterInput($filter_id)
{
$this->set('filter_id', $filter_id);
$this->viewBuilder()->setLayout('ajax');
}
}

236
idrocap_wa/src/Controller/GroupsController.php

@ -0,0 +1,236 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\View\JsonView;
use App\View\CustomCsvView;
use Cake\I18n\DateTime;
use Cake\Http\Exception\ForbiddenException;
/**
* Groups Controller
*
* @property \App\Model\Table\GroupsTable $Groups
* @method \App\Model\Entity\Group[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class GroupsController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class, CustomCsvView::class];
}
/**
* index
*
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function index()
{
if (!$this->logged_user->hasCapability('configuration.groups.read')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$groups = $this->Groups->find()
->contain(['Actors', 'ChildGroups']);
if (!$this->getRequest()->is(['json', 'xml', 'csv'])) {
$groups = $groups
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
return $results->map(function ($row) {
$row->can_edit = $this->logged_user->hasCapability('configuration.groups.edit');
$row->can_delete = $this->logged_user->hasCapability('configuration.groups.delete');
$row->child_groups_csv = collection($row->child_groups)->reduce(function ($csv, $child_group) { return empty($csv) ? $child_group->description : ($csv . ', ' . $child_group->description); }, '');
return $row;
});
});
}
$this->paginate = [
'sortableFields' => ['description', 'is_default'],
];
$this->set('groups', $this->getRequest()->is(['json', 'xml', 'csv']) ? $groups : $this->paginate($groups));
if ($this->getRequest()->is('csv')) {
if (!$this->logged_user->hasCapability('configuration.groups.export_csv')) throw new ForbiddenException(__('Non hai i permessi necessari'));
// docs here: https://github.com/FriendsOfCake/cakephp-csvview
$header = [
__('Descrizione'),
__('Profilo Default'),
__('Profili Associati'),
];
$extract = [
'description',
function (array $row) {
return $row['is_default'] ? __('SI') : __('NO');
},
function (array $row) {
return collection($row['child_groups'])->reduce(function ($csv, $child_group) { return empty($csv) ? $child_group['description'] : ($csv . ', ' . $child_group['description']); }, '');
},
];
$this->viewBuilder()
->setClassName(CustomCsvView::class)
->setOptions([
'header' => $header,
'extract' => $extract,
]);
$dateTime = DateTime::now();
$timestamp = $dateTime->i18nFormat('Y-M-d-H-m-ss', $this->logged_user->timezone);
$this->setResponse($this->getResponse()->withDownload("profiles_$timestamp.csv"));
}
$this->viewBuilder()->setOption('serialize', 'groups');
}
/**
* view
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function view($id = null)
{
if (!$this->logged_user->hasCapability('configuration.groups.read')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$group = $this->Groups->get($id, contain: ['Actors', 'ChildGroups']);
$group->can_handle_capabilities = $this->logged_user->hasCapability('configuration.groups.capabilities');
$group->can_edit = $this->logged_user->hasCapability('configuration.groups.edit');
$group->can_delete = $this->logged_user->hasCapability('configuration.groups.delete');
$group->child_groups_csv = collection($group->child_groups)->reduce(function ($csv, $child_group) { return empty($csv) ? $child_group->description : ($csv . ', ' . $child_group->description); }, '');
$this->set(compact('group'));
$this->viewBuilder()->setOption('serialize', 'group');
}
/**
* add
*
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function add()
{
if (!$this->logged_user->hasCapability('configuration.groups.add')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$group = $this->Groups->newEmptyEntity();
if ($this->request->is('post')) {
$group = $this->Groups->patchEntity($group, $this->request->getData());
if ($this->Groups->save($group)) {
$this->Flash->success(__('Profilo Utente creato con successo.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Errore durante la creazione del Profilo Utente'));
}
$child_groups = $this->fetchTable('Groups')->find('list')->contain(['Actors'])->order(['Groups.description' => 'ASC']);
$this->set(compact('group'));
$this->set(compact('child_groups'));
}
/**
* edit
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
if (!$this->logged_user->hasCapability('configuration.groups.edit')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$group = $this->Groups->get($id, contain: ['Actors', 'ChildGroups']);
if ($this->request->is(['patch', 'post', 'put'])) {
$group = $this->Groups->patchEntity($group, $this->request->getData());
if ($this->Groups->save($group)) {
$this->Flash->success(__('Profilo Utente "{0}" modificato con successo.', $group->description));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Errore durante la modifica del Profilo Utente "{0}". Riprovare di nuovo.', $group->description));
}
$child_groups = $this->fetchTable('Groups')->find('list')->contain(['Actors'])->order(['Groups.description' => 'ASC']);
$this->set(compact('group'));
$this->set(compact('child_groups'));
}
/**
* delete
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function delete($id = null)
{
if (!$this->logged_user->hasCapability('configuration.groups.delete')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$this->request->allowMethod(['post', 'delete']);
$group = $this->Groups->get($id, contain: ['Actors']);
if ($this->Groups->Actors->delete($group->actor)) {
$this->Flash->success(__('Il Profilo Utente "{0}" è stato cancellato con successo', $group->description));
} else {
$errors = $group->actor->getErrors();
if (isset($errors['is_editable']['checkGroupIsEditable'])) {
$errorMessage = $errors['is_editable']['checkGroupIsEditable'];
$this->Flash->error($errorMessage);
} else {
$this->Flash->error(__('Errore durante la cancellazione del Profilo Utente "{0}". Riprovare di nuovo.', $group->description));
}
}
return $this->redirect(['action' => 'index']);
}
/**
* handleCapabilities
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function handleCapabilities($id = null)
{
if (!$this->logged_user->hasCapability('configuration.groups.capabilities')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$group = $this->Groups->get($id, contain: ['Actors']);
$group_capability_ids = $this->Groups->Capabilities->find()
->select(['id'])
->matching('Groups')
->where(['Groups.id' => $id]);
if (!$this->logged_user->sys_admin) {
$group_capability_ids = $group_capability_ids
->where(['Capabilities.is_configurable' => 1]);
}
$group_capability_ids = $group_capability_ids
->distinct()
->all()
->extract('id')
->toArray();
if ($this->request->is(['patch', 'post', 'put'])) {
$group = $this->Groups->patchEntity($group, $this->request->getData(), ['group_id' => $group->id]);
if ($this->Groups->save($group)) {
$this->Flash->success(__('Profilo Utente "{0}" modificato con successo.', $group->description));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Errore durante la modifica del Profilo Utente "{0}". Riprovare di nuovo.', $group->description));
}
$capability_groups = $this->Groups->Capabilities->CapabilityGroups->find()
->contain(['Capabilities' => function ($q) {
if (!$this->logged_user->sys_admin) {
$q->where(['Capabilities.is_configurable' => 1]);
}
return $q->order(['Capabilities.capability_group_id' => 'ASC', 'Capabilities.priority_group_id' => 'ASC', 'Capabilities.priority' => 'ASC', 'Capabilities.description']);
}])
->order(['CapabilityGroups.description' => 'ASC']);
$this->set(compact('group', 'group_capability_ids', 'capability_groups'));
$this->viewBuilder()->setOption('serialize', 'group');
}
}

187
idrocap_wa/src/Controller/MapsController.php

@ -0,0 +1,187 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Core\Configure;
use Cake\Http\Client;
use Cake\Http\Response;
use App\WGS\Geo\Geocoding;
use App\WGS\Geo\GeoValidation;
use App\WGS\Geo\LocationAttributesRetriever;
use App\WGS\Geo\IntGeoResourceAttributesRetriever;
use App\WGS\Geo\ExtGeoResourceAttributesRetriever;
use Cake\Http\Exception\BadRequestException;
use Cake\Http\Exception\NotFoundException;
/**
* Map Controller
*
*/
class MapsController extends AppController
{
private $geo_resources = [];
/**
* geoserver_proxy
*
* @param String $query_parameters
* @return String
*/
private function geoserver_proxy(String $query_parameters): String
{
$url = Configure::read('App.geoserverUrl') . Configure::read('App.geoserverAPP') . 'wfs?';
$query_parameters = str_replace([' ', '>'], ['%20', '%3E'], $query_parameters);
$url .= $query_parameters;
$http = new Client();
$response = $http->get($url, [], [
'auth' => ['username' => Configure::read('App.geoserverUsername'), 'password' => Configure::read('App.geoserverPassword')]
]);
return $response->getStringBody();
}
/**
* get_location_attributes
*
* @param String $lat
* @param String $lon
* @return ?Array
*/
private function get_location_attributes(String $lat, String $lon): ?Array {
$feature_collection = '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[' . $lon . ',' . $lat . ']},"properties":null}]}';
$location = $this->fetchTable('Locations')->newEntity([
'feature_collection' => $feature_collection,
]);
$lar = new LocationAttributesRetriever($location);
$lar_result = $lar->retrieve()[0] ?? null;
return $lar_result;
}
/**
* get_matching_geo_resources
*
* @param String $lat
* @param String $lon
* @return ?Array
*/
private function get_matching_geo_resources(String $lat, String $lon): ?Array {
$feature_collection = '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[' . $lon . ',' . $lat . ']},"properties":null}]}';
$matching_geo_resources = [];
// inizializziamo l'array $mapped_fields con un array associativo con KEY id del campo e VALUE '':
$mapped_fields = collection($this->geo_resources)->reduce(function ($acc, $geo_resource) {
return array_merge($acc, array_fill_keys(array_values($geo_resource['attributes_fields']), ''));
}, []);
foreach ($this->geo_resources as $geo_resource) {
if (isset($geo_resource['external']) && is_bool($geo_resource['external']) && $geo_resource['external']) {
$grar = new ExtGeoResourceAttributesRetriever($geo_resource['url'], $geo_resource['lon_parameter_name'], $geo_resource['lat_parameter_name']);
$grar_result = $grar->retrieve($lon, $lat);
if (isset($grar_result)) {
// scremiamo il risultato proveniente dal servizio esterno considerando solo gli attributi che ci interessano:
$grar_result = array_intersect_key($grar_result, $geo_resource['attributes_fields']);
$matching_geo_resources[$geo_resource['url']] = $grar_result;
foreach ($grar_result as $attribute_name => $attribute_value) {
$mapped_fields[$geo_resource['attributes_fields'][$attribute_name]] = $attribute_value;
}
}
} else {
$grar = new IntGeoResourceAttributesRetriever($geo_resource['table_name'], $geo_resource['geometry_name'], array_keys($geo_resource['attributes_fields']));
$grar_result = $grar->retrieve($feature_collection)[0] ?? null;
if (isset($grar_result)) {
$matching_geo_resources[$geo_resource['table_name']] = $grar_result;
foreach ($grar_result as $attribute_name => $attribute_value) {
$mapped_fields[$geo_resource['attributes_fields'][$attribute_name]] = $attribute_value;
}
}
}
}
return ['matching_geo_resources' => $matching_geo_resources, 'mapped_fields' => $mapped_fields];
}
/**
* map_geocoding_result_content
*
* @param Array $content
* @return Array
*/
private function map_geocoding_result_content(Array $content): Array {
$array_content = explode(", ", $content['display_name'] ?? '');
$index_cursor = count($array_content) - 1;
if ($index_cursor > 0) $nation = $array_content[$index_cursor--];
if ($index_cursor > 0) $cap = (Int)$array_content[$index_cursor] > 0 ? $array_content[$index_cursor--] : '';
if ($index_cursor > 0) $region = $array_content[$index_cursor--];
if ($index_cursor > 0) $province = $array_content[$index_cursor--];
if ($index_cursor > 0) $district = $array_content[$index_cursor--];
$address = $content['namedetails']['name'] ?? '';
$description = $content['display_name'] ?? '';
$lat = $content['lat'] ?? '';
$lon = $content['lon'] ?? '';
return ['nation' => $nation ?? '', 'cap' => $cap ?? '', 'region' => $region ?? '', 'province' => $province ?? '', 'district' => $district ?? '', 'address' => $address, 'lat' => $lat, 'lon' => $lon, 'description' => $description];
}
/**
* index
*
* @return \Cake\Http\Response
*/
public function index () {
if(!$this->logged_user->hasCapability(['confiiguration.maps.view'])) throw new BadRequestException(__('Non hai i permessi'));
}
/**
* getWfs
*
* @return Response
*/
public function getWfs(): Response {
if(!$this->logged_user->hasCapability(['confiiguration.maps.view'])) throw new BadRequestException(__('Non hai i permessi'));
$response = $this->geoserver_proxy($this->request->getUri()->getQuery());
return $this->getResponse()->withStringBody($response)->withType("application/json");
}
/**
* geocode
*
* @throws BadRequestException
* @throws NotFoundException
* @return Response
*/
public function geocode(): Response {
if(!$this->logged_user->hasCapability(['confiiguration.maps.view'])) throw new BadRequestException(__('Non hai i permessi'));
// geo_resources, se presente, contiene le info per mappare campi aggiuntivi con attributi di
// una o + geo-risorse in base a dove il punto specificato ricade:
$geo_resources = json_decode($this->request->getQuery('geo_resources') ?? '', true);
if ($geo_resources !== null && is_array($geo_resources) && count($geo_resources) > 0) $this->geo_resources = $geo_resources;
if ($this->request->getQuery('address') === null) throw new BadRequestException('geocode error: invalid address');
$separator = null;
if (count(collection(explode(" ", $this->request->getQuery('address')))->filter(function ($part) { return is_numeric(trim($part)); })->toArray()) === 2) {
$separator = " ";
}
if (count(collection(explode(",", $this->request->getQuery('address')))->filter(function ($part) { return is_numeric(trim($part)); })->toArray()) === 2) {
$separator = ",";
}
if ($separator !== null) {
$address_parts = explode($separator, $this->request->getQuery('address'));
$lat = $address_parts[0] > $address_parts[1] ? $address_parts[0] : $address_parts[1];
$lon = $address_parts[0] > $address_parts[1] ? $address_parts[1] : $address_parts[0];
$feature_collection = '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[' . $lon . ',' . $lat . ']},"properties":null}]}';
if (!GeoValidation::isValidGeometry(json_decode($feature_collection))) throw new BadRequestException('invalid coordinates or out of boundary!');
$reverse_geocoding = Geocoding::performGeocoding('reverse', ['format' => 'json', 'namedetails' => '1', 'lat' => $lat, 'lon' => $lon]);
if (!empty($reverse_geocoding['contents'])) {
$mapped_geocoding_result_content = $this->map_geocoding_result_content($reverse_geocoding['contents']);
}
$lar_result = $this->get_location_attributes($lat, $lon);
$content = ['geo_resources' => $this->get_matching_geo_resources($lat, $lon), 'nation' => $mapped_geocoding_result_content['nation'] ?? '', 'cap' => $mapped_geocoding_result_content['cap'] ?? '', 'region' => $lar_result['region'] ?? $mapped_geocoding_result_content['region'] ?? '', 'province' => $lar_result['county_name'] ?? $mapped_geocoding_result_content['province'] ?? '', 'district_code' => $lar_result['district_code'] ?? '', 'district' => $lar_result['district_name'] ?? $mapped_geocoding_result_content['district'] ?? '', 'address' => $mapped_geocoding_result_content['address'] ?? '', 'lat' => $lat, 'lon' => $lon, 'description' => !empty($mapped_geocoding_result_content['description']) ? $mapped_geocoding_result_content['description'] : __('Punto alle coordinate (EPSG:4326) Latitudine: {0}, Longitudine: {1}', $lat, $lon)];
return $this->getResponse()->withStringBody(json_encode($content))->withType("application/json");
}
$geocoding = Geocoding::performGeocoding('search', ['format' => 'json', 'namedetails' => '1', 'q' => $this->request->getQuery('address')]);
if (empty($geocoding['contents'])) throw new NotFoundException('geocode error: no contents');
return $this->getResponse()->withStringBody(json_encode($geocoding['contents']))->withType("application/json");
}
}

79
idrocap_wa/src/Controller/MobilePhonesController.php

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
/**
* MobilePhones Controller
*
* @property \App\Model\Table\MobilePhonesTable $MobilePhones
*/
class MobilePhonesController extends AppController
{
/**
* add
*
* @param Int $actor_id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function add($actor_id)
{
$actor = $this->MobilePhones->Deliveries->Actors->get($actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canAddContactsForActor($actor, 'mobile_phone')) throw new ForbiddenException(__('Non hai i permessi per aggiungere i recapiti di questo tipo per {0}', $actor_type_description));
$mobilePhone = $this->MobilePhones->newEntity([
'delivery' => [
'actor_id' => $actor_id,
'delivery_type_id' => 1,
],
], ['validate' => false]);
if ($this->request->is('post')) {
$mobilePhone = $this->MobilePhones->patchEntity($mobilePhone, $this->request->getData());
if ($this->MobilePhones->save($mobilePhone)) {
$this->Flash->success(__('Il Cellulare è stato aggiunto con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $actor_id]);
}
$this->Flash->error(__('Errore durante l\'aggiunta del Cellulare'));
}
$mobilePhoneDescriptions = $this->MobilePhones->MobilePhoneDescriptions->find('list');
$this->set(compact('actor', 'mobilePhone', 'mobilePhoneDescriptions'));
}
/**
* edit
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
$mobilePhone = $this->MobilePhones->get($id, contain: ['Deliveries' => ['Actors' => ['ActorTypes']]]);
$actor = $this->MobilePhones->Deliveries->Actors->get($mobilePhone->delivery->actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canEditContactsForActor($actor, 'mobile_phone')) throw new ForbiddenException(__('Non hai i permessi per modificare i recapiti di questo tipo per {0}', $actor_type_description));
if ($this->request->is(['patch', 'post', 'put'])) {
$mobilePhone = $this->MobilePhones->patchEntity($mobilePhone, $this->request->getData());
if ($this->MobilePhones->save($mobilePhone)) {
$this->Flash->success(__('Il Cellulare è stato modificato con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $mobilePhone->delivery->actor_id]);
}
$this->Flash->error(__('Errore durante la modifica del Cellulare. Riprovare di nuovo'));
}
$mobilePhoneDescriptions = $this->MobilePhones->MobilePhoneDescriptions->find('list');
$this->set(compact('actor', 'mobilePhone', 'mobilePhoneDescriptions'));
}
}

97
idrocap_wa/src/Controller/NotificationsController.php

@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\I18n\DateTime;
use Cake\View\JsonView;
/**
* Notifications Controller
*
* @property \App\Model\Table\NotificationsTable $Notifications
* @method \App\Model\Entity\Notification[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class NotificationsController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class];
}
/**
* getUserNotifications
*
* @return Array
*/
private function getUserNotifications()
{
return $this->fetchTable('Notifications')
->find()
->contain('NotificationTypes')
->where(['Notifications.user_id' => $this->logged_user->id, 'Notifications.read_by_user IS NULL'])
->order(['Notifications.created' => 'DESC'])
->all()
->reduce(function ($acc, $user_notification) {
$notification_info = json_decode($user_notification->info);
$body = strlen($notification_info->body) > 200 ? substr($notification_info->body, 0, 197) . '...' : $notification_info->body;
$icon = $user_notification->notification_type->icon ?? 'bell';
$time_ago = (new DateTime($user_notification->created))->timeAgoInWords();
$acc[] = ['id' => $user_notification->id, 'icon' => $icon, 'title' => $notification_info->title, 'body' => $body, 'time_ago' => $time_ago];
return $acc;
}, []);
}
/**
* readAll
*
* @return \Cake\Http\Response
*/
public function readAll()
{
$this->fetchTable('Notifications')->updateAll(['read_by_user' => new DateTime()], ['user_id' => $this->logged_user->id, 'read_by_user IS NULL']);
$user_notifications = $this->getUserNotifications();
$this->set('user_notifications', $user_notifications);
$this->viewBuilder()->setOption('serialize', 'user_notifications')->setLayout('ajax');
}
/**
* testAll
*
* @return \Cake\Http\Response
*/
public function testAll()
{
$this->request->allowMethod(['post']);
$this->sendNotifications(null, 'test_all', []);
return $this->redirect('/');
}
/**
* view
*
* @todo recuperare la specializzazione del CO e fare redirect alla view dell'oggetto specifico!
* @param Int $id
* @return \Cake\Http\Response
*/
public function view($id)
{
$notification = $this->fetchTable('Notifications')->get($id);
$notification->read_by_user = new DateTime();
if (!$this->fetchTable('Notifications')->save($notification)) {
$this->Flash->error(__('Errore durante la cancellazione della notifica'));
}
$notification_info = json_decode($notification->info);
$link = !empty($notification_info->link) ? $notification_info->link : '/';
return $this->redirect($link);
}
public function index()
{
$user_notifications = $this->getUserNotifications();
$this->set('user_notifications', $user_notifications);
$this->viewBuilder()->setOption('serialize', 'user_notifications')->setLayout('ajax');
}
}

204
idrocap_wa/src/Controller/OrganisationsController.php

@ -0,0 +1,204 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\View\JsonView;
use App\View\CustomCsvView;
use Cake\I18n\DateTime;
use Cake\Http\Exception\ForbiddenException;
/**
* Organisations Controller
*
* @property \App\Model\Table\OrganisationsTable $Organisations
* @method \App\Model\Entity\Organisation[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class OrganisationsController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class, CustomCsvView::class];
}
/**
* index
*
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function index()
{
if (!$this->logged_user->hasCapability('configuration.organisations.read')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$organisations = $this->Organisations->find()
->contain(['OrganisationTypes', 'Actors']);
$this->set('total_organisations', $organisations->count());
// applichiamo gli eventuali filtri presenti:
$organisations = $this->applyFilters($organisations);
$this->set('filtered_organisations', $organisations->count());
if (!$this->getRequest()->is(['json', 'xml', 'csv'])) {
$organisations = $organisations
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
return $results->map(function ($row) {
$row->can_edit = $this->logged_user->organisation_id == $row->id || $this->logged_user->hasCapability('configuration.organisations.edit');
$row->can_delete = $this->logged_user->organisation_id == $row->id || $this->logged_user->hasCapability('configuration.organisations.delete');
return $row;
});
});
}
$this->paginate = [
'sortableFields' => ['Actors.description', 'acronym', 'OrganisationTypes.description', 'address', 'district', 'cap', 'province'],
];
$this->set('organisations', $this->getRequest()->is(['json', 'xml', 'csv']) ? $organisations : $this->paginate($organisations));
if ($this->getRequest()->is('csv')) {
if (!$this->logged_user->hasCapability('configuration.organisations.export_csv')) throw new ForbiddenException(__('Non hai i permessi necessari'));
// docs here: https://github.com/FriendsOfCake/cakephp-csvview
$header = [
__('Descrizione'),
__('Acronimo'),
__('Tipologia'),
__('Indirizzo'),
__('Comune'),
__('CAP'),
__('Provincia'),
__('Recapiti'),
];
$extract = [
function (array $row) {
return $row['actor']['description'];
},
'acronym',
function (array $row) {
return $row['organisation_type']['description'];
},
'address',
'district',
'cap',
'province',
function (array $row) {
return $row['actor']['contacts'];
},
];
$this->viewBuilder()
->setClassName(CustomCsvView::class)
->setOptions([
'header' => $header,
'extract' => $extract,
]);
$dateTime = DateTime::now();
$timestamp = $dateTime->i18nFormat('Y-M-d-H-m-ss', $this->logged_user->timezone);
$this->setResponse($this->getResponse()->withDownload("organisations_$timestamp.csv"));
}
$this->viewBuilder()->setOption('serialize', 'organisations');
}
/**
* view
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function view($id = null)
{
if ($this->logged_user->organisation_id != $id && !$this->logged_user->hasCapability('configuration.organisations.read')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$organisation = $this->Organisations->get($id, contain: ['ControllableObjectInterfaces', 'OrganisationTypes', 'Actors']);
$organisation->can_handle_contacts = $this->logged_user->canViewContactsForActor($this->Organisations->Actors->get($organisation->actor_id, contain: ['Organisations']));
$organisation->can_edit = $this->logged_user->organisation_id == $id || $this->logged_user->hasCapability('configuration.organisations.edit');
$organisation->can_delete = $this->logged_user->organisation_id == $id || $this->logged_user->hasCapability('configuration.organisations.delete');
$organisationTypes = $this->Organisations->OrganisationTypes->find('list')->order(['OrganisationTypes.description' => 'ASC']);
$this->set(compact('organisation', 'organisationTypes'));
}
/**
* add
*
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function add()
{
if (!$this->logged_user->hasCapability('configuration.organisations.add')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$organisation = $this->Organisations->newEmptyEntity();
if ($this->request->is('post')) {
$organisation = $this->Organisations->patchEntity($organisation, $this->request->getData());
if ($this->Organisations->save($organisation)) {
$this->Flash->success(__('Organizzazione creata con successo.'));
return $this->redirect(['action' => 'index']);
}
$errorMessage = $organisation->getError('attachment')[0] ?? '';
$this->Flash->error(__('Errore durante la creazione dell\'organizzazione. ' . $errorMessage));
}
$organisationTypes = $this->Organisations->OrganisationTypes->find('list')->order(['OrganisationTypes.description' => 'ASC']);
$this->set(compact('organisation', 'organisationTypes'));
}
/**
* edit
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
if ($this->logged_user->organisation_id != $id && !$this->logged_user->hasCapability('configuration.organisations.edit')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$organisation = $this->Organisations->get($id, contain: ['Actors', 'ControllableObjectInterfaces', 'OrganisationTypes']);
if ($this->request->is(['patch', 'post', 'put'])) {
$organisation = $this->Organisations->patchEntity($organisation, $this->request->getData());
if ($this->Organisations->save($organisation)) {
$this->Flash->success(__('Organizzazione "{0}" modificata con successo.', $organisation->actor->description));
return $this->redirect(['action' => 'index']);
}
$errMessage = $organisation->getError('attachment')[0] ?? '';
$this->Flash->error(__('Errore durante la modifica dell\'organizzazione "{0}". Riprovare di nuovo. ', $organisation->actor->description) . $errMessage);
}
$organisationTypes = $this->Organisations->OrganisationTypes->find('list')->order(['OrganisationTypes.description' => 'ASC']);
$this->set(compact('organisation', 'organisationTypes'));
}
/**
* delete
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function delete($id = null)
{
if ($this->logged_user->organisation_id != $id && !$this->logged_user->hasCapability('configuration.organisations.delete')) throw new ForbiddenException(__('Non hai i permessi necessari'));
$this->request->allowMethod(['post', 'delete']);
$organisation = $this->Organisations->get($id, contain: ['Actors']);
// controlliamo se il type è cittadini e preveniamo la possibilità di delete.
if($organisation->organisation_type_id === 7) {
$this->Flash->error(__('Impossibile eliminare l\'organizzazione "{0}".', $organisation->actor->description));
return $this->redirect(['action' => 'index']);
}
$are_there_users_in_this_org = $this->fetchTable('Users')->find()->where(['Users.organisation_id' => $id])->count();
if ($are_there_users_in_this_org > 0) {
$this->Flash->error(__('Impossibile eliminare l\'organizzazione "{0}". Ci sono {1} utenti collegati ad essa.', $organisation->actor->description, $are_there_users_in_this_org));
return $this->redirect(['action' => 'index']);
}
if ($this->Organisations->Actors->delete($organisation->actor)) {
$this->Flash->success(__('L\'organizzazione "{0}" è stata cancellata con successo', $organisation->actor->description));
} else {
$this->Flash->error(__('Errore durante la cancellazione dell\'organizzazione "{0}". Riprovare di nuovo.', $organisation->actor->description));
}
return $this->redirect(['action' => 'index']);
}
}

78
idrocap_wa/src/Controller/PecsController.php

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
/**
* Pecs Controller
*
* @property \App\Model\Table\PecsTable $Pecs
*/
class PecsController extends AppController
{
/**
* add
*
* @param Int $actor_id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function add($actor_id)
{
$actor = $this->Pecs->Deliveries->Actors->get($actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canAddContactsForActor($actor, 'pec')) throw new ForbiddenException(__('Non hai i permessi per aggiungere i recapiti di questo tipo per {0}', $actor_type_description));
$pec = $this->Pecs->newEntity([
'delivery' => [
'actor_id' => $actor_id,
'delivery_type_id' => 6,
],
], ['validate' => false]);
if ($this->request->is('post')) {
$pec = $this->Pecs->patchEntity($pec, $this->request->getData());
if ($this->Pecs->save($pec)) {
$this->Flash->success(__('La PEC è stata aggiunta con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $actor_id]);
}
$this->Flash->error(__('Errore durante l\'aggiunta della PEC'));
}
$pecDescriptions = $this->Pecs->PecDescriptions->find('list');
$this->set(compact('actor', 'pec', 'pecDescriptions'));
}
/**
* edit
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
$pec = $this->Pecs->get($id, contain: ['Deliveries' => ['Actors' => ['ActorTypes']]]);
$actor = $this->Pecs->Deliveries->Actors->get($pec->delivery->actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canEditContactsForActor($actor, 'pec')) throw new ForbiddenException(__('Non hai i permessi per modificare i recapiti di questo tipo per {0}', $actor_type_description));
if ($this->request->is(['patch', 'post', 'put'])) {
$pec = $this->Pecs->patchEntity($pec, $this->request->getData());
if ($this->Pecs->save($pec)) {
$this->Flash->success(__('La PEC è stata modificata con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $pec->delivery->actor_id]);
}
$this->Flash->error(__('Errore durante la modifica della PEC. Riprovare di nuovo'));
}
$pecDescriptions = $this->Pecs->PecDescriptions->find('list');
$this->set(compact('actor', 'pec', 'pecDescriptions'));
}
}

78
idrocap_wa/src/Controller/PhonesController.php

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
/**
* Phones Controller
*
* @property \App\Model\Table\PhonesTable $Phones
*/
class PhonesController extends AppController
{
/**
* add
*
* @param Int $actor_id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function add($actor_id)
{
$actor = $this->Phones->Deliveries->Actors->get($actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canAddContactsForActor($actor, 'phone')) throw new ForbiddenException(__('Non hai i permessi per aggiungere i recapiti di questo tipo per {0}', $actor_type_description));
$phone = $this->Phones->newEntity([
'delivery' => [
'actor_id' => $actor_id,
'delivery_type_id' => 4,
],
], ['validate' => false]);
if ($this->request->is('post')) {
$phone = $this->Phones->patchEntity($phone, $this->request->getData());
if ($this->Phones->save($phone)) {
$this->Flash->success(__('Il Telefono è stato aggiunto con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $actor_id]);
}
$this->Flash->error(__('Errore durante l\'aggiunta del Telefono'));
}
$phoneDescriptions = $this->Phones->PhoneDescriptions->find('list');
$this->set(compact('actor', 'phone', 'phoneDescriptions'));
}
/**
* edit
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
$phone = $this->Phones->get($id, contain: ['Deliveries' => ['Actors' => ['ActorTypes']]]);
$actor = $this->Phones->Deliveries->Actors->get($phone->delivery->actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canEditContactsForActor($actor, 'phone')) throw new ForbiddenException(__('Non hai i permessi per modificare i recapiti di questo tipo per {0}', $actor_type_description));
if ($this->request->is(['patch', 'post', 'put'])) {
$phone = $this->Phones->patchEntity($phone, $this->request->getData());
if ($this->Phones->save($phone)) {
$this->Flash->success(__('Il Telefono è stato modificato con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $phone->delivery->actor_id]);
}
$this->Flash->error(__('Errore durante la modifica del Telefono. Riprovare di nuovo'));
}
$phoneDescriptions = $this->Phones->PhoneDescriptions->find('list');
$this->set(compact('actor', 'phone', 'phoneDescriptions'));
}
}

174
idrocap_wa/src/Controller/PrivacyController.php

@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Event\EventInterface;
use Cake\Http\Exception\ForbiddenException;
use Cake\Datasource\ConnectionManager;
use Exception;
/**
* Privacy Controller
*
*/
class PrivacyController extends AppController
{
public function beforeFilter(EventInterface $event)
{
$this->Authentication->allowUnauthenticated(['view_public']);
}
/**
* Index method
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function view_public()
{
$privacy = $this->Privacy->findPrivacy();
$this->viewBuilder()->setLayout('view_public');
$this->set(compact('privacy'));
}
/**
* View method
*
* @return \Cake\Http\Response|null|void Renders view
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view()
{
$breadcrumbs = $this->retrieveBreadcrumbs('view');
$privacy = $this->Privacy->findPrivacy();
$this->set(compact('privacy', 'breadcrumbs'));
}
/**
* Edit method
*
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit()
{
if (!$this->logged_user->hasCapability(['configuration.privacy.edit'])) {
throw new ForbiddenException(__('Non hai i permessi necessari'));
}
$breadcrumbs = $this->retrieveBreadcrumbs('edit');
$privacy = $this->Privacy->findPrivacy();
if ($this->request->is(['patch', 'post', 'put'])) {
$privacy = $this->Privacy->patchEntity($privacy, $this->request->getData());
try {
$connection = ConnectionManager::get('default');
$connection->begin();
if ($this->Privacy->save($privacy)) {
$this->Privacy->PrivacyUsers->deleteAll(['privacy_id' => $privacy->id]);
$connection->commit();
$this->Flash->success(__('La privacy è stata salvata con successo.'));
} else {
$connection->rollback();
$this->Flash->error(__('La privacy non è stata salvata. Riprova, per favore.'));
}
} catch(Exception $e) {
$connection->rollback();
$this->Flash->error(__('Errore. La privacy non è stata salvata.'));
}
}
$this->set(compact('privacy', 'breadcrumbs'));
}
public function accept_privacy()
{
$hasConsent = $this->Privacy->PrivacyUsers->hasConsent($this->logged_user->id);
if ($hasConsent) {
$this->Flash->error(__('Hai già accettato la privacy.'));
return $this->redirect(['controller' => 'Dashboard', 'action' => 'index']);
}
$breadcrumbs = $this->retrieveBreadcrumbs('accept_privacy');
$privacy = $this->Privacy->findPrivacy();
if (!$privacy) {
$this->Flash->error(__('La privacy non è disponibile.'));
return $this->redirect(['controller' => 'Dashboard', 'action' => 'index']);
}
if ($this->request->is(['post', 'put', 'patch'])) {
if ($this->Privacy->PrivacyUsers->saveConsent($this->logged_user->id, $privacy->id)) {
$this->Flash->success(__('La privacy è stata accettata con successo.'));
$this->redirect(['controller' => 'Dashboard', 'action' => 'index']);
} else {
$this->Flash->error(__('Errore. La privacy non è stata accettata.'));
}
}
$this->set(compact('privacy', 'breadcrumbs'));
}
private function retrieveBreadcrumbs(string $action): array
{
$breadcrumbs = [
'edit' => [
[
'title' => 'Home',
'icon' => 'fa fa-home',
'url' => '/',
],
[
'title' => __('Configurazioni'),
'icon' => 'fa fa-wrench',
],
[
'title' => __('Privacy'),
'icon' => 'fa fa-wrench',
],
[
'title' => __('Modifica privacy'),
'icon' => 'fa fa-pencil-alt',
],
],
'view' =>
[
[
'title' => 'Home',
'icon' => 'fa fa-home',
'url' => '/',
],
[
'title' => __('Configurazioni'),
'icon' => 'fa fa-wrench',
],
[
'title' => __('Privacy'),
'icon' => 'fa fa-wrench',
],
[
'title' => __('Visualizza privacy'),
'icon' => 'fa fa-info',
],
],
'accept_privacy' => [
[
'title' => 'Home',
'icon' => 'fa fa-home',
'url' => '/',
],
[
'title' => __('Configurazioni'),
'icon' => 'fa fa-wrench',
],
[
'title' => __('Privacy'),
'icon' => 'fa fa-wrench',
],
[
'title' => __('Visualizzazione privacy'),
'icon' => 'fa fa-info',
],
]
];
return $breadcrumbs[$action] ?? [];
}
}

91
idrocap_wa/src/Controller/TagsController.php

@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
/**
* Tags Controller
*
* @property \App\Model\Table\TagsTable $Tags
*/
class TagsController extends AppController
{
/**
* Index method
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function index()
{
if (!$this->logged_user->hasCapability(['configuration.tags.view'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$tags = $this->Tags->find()->formatResults(function (\Cake\Collection\CollectionInterface $results) {
return $results->map(function ($row) {
$row->can_edit = $this->logged_user->hasCapability('configuration.tags.edit');
$row->can_view = $this->logged_user->hasCapability('configuration.tags.disable');
return $row;
});
});
$tags = $this->paginate($tags);
$this->set(compact('tags'));
}
/**
* View method
*
* @param string|null $id Tag id.
* @return \Cake\Http\Response|null|void Renders view
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($id = null)
{
if (!$this->logged_user->hasCapability(['configuration.tags.view'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$tag = $this->Tags->get($id);
$this->set(compact('tag'));
}
/**
* Add method
*
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
*/
public function add()
{
if (!$this->logged_user->hasCapability(['configuration.tags.add'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$tag = $this->Tags->newEmptyEntity();
if ($this->request->is('post')) {
$tag = $this->Tags->patchEntity($tag, $this->request->getData());
if ($this->Tags->save($tag, ['associated' => ['ControllableObjects']])) {
$this->Flash->success(__('Il nuovo tag è stato creato'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Errore nella creazione del nuovo tag.') . json_encode($tag->getErrors()));
}
$this->set(compact('tag'));
}
/**
* Edit method
*
* @param string|null $id Tag id.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit($id = null)
{
if (!$this->logged_user->hasCapability(['configuration.tags.edit'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$tag = $this->Tags->find()->contain(['ControllableObjects'])->where(['Tags.id' => $id])->first();
if ($this->request->is(['patch', 'post', 'put'])) {
$tag = $this->Tags->patchEntity($tag, $this->request->getData(), ['filename_as_tag' => true, 'associated' => ['ControllableObjects' => ['associated' => ['Attachments']]]]);
if ($this->Tags->save($tag)) {
$this->Flash->success(__('Il tag è stato salvato.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Errore durante il salvataggio del tag.') . json_encode($tag->getErrors()));
}
$this->set(compact('tag'));
}
}

76
idrocap_wa/src/Controller/TelegramChatsController.php

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Http\Exception\ForbiddenException;
/**
* TelegramChats Controller
*
* @property \App\Model\Table\TelegramChatsTable $TelegramChats
*/
class TelegramChatsController extends AppController
{
/**
* add
*
* @param Int $actor_id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function add($actor_id)
{
$actor = $this->TelegramChats->Deliveries->Actors->get($actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canAddContactsForActor($actor, 'telegram_chat')) throw new ForbiddenException(__('Non hai i permessi per aggiungere i recapiti di questo tipo per {0}', $actor_type_description));
$telegram_chat = $this->TelegramChats->newEntity([
'delivery' => [
'actor_id' => $actor_id,
'delivery_type_id' => 5,
],
], ['validate' => false]);
if ($this->request->is('post')) {
$telegram_chat = $this->TelegramChats->patchEntity($telegram_chat, $this->request->getData());
if ($this->TelegramChats->save($telegram_chat)) {
$this->Flash->success(__('La Chat Telegram è stata aggiunta con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $actor_id]);
}
$this->Flash->error(__('Errore durante l\'aggiunta della Chat Telegram'));
}
$this->set(compact('actor', 'telegram_chat'));
}
/**
* edit
*
* @param Int $id
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
$telegram_chat = $this->TelegramChats->get($id, contain: ['Deliveries' => ['Actors' => ['ActorTypes']]]);
$actor = $this->TelegramChats->Deliveries->Actors->get($telegram_chat->delivery->actor_id, contain: ['ActorTypes', 'Organisations', 'Users']);
// se arriviamo in questa pagina con un actor_id non di tipo "organizzazione" o "utente":
if (!in_array($actor->actor_type_id, [1,5])) throw new ForbiddenException(__('Gestione recapiti non prevista per questa tipologia di oggetto'));
$actor_type_description = $actor->actor_type_id == 1 ? __('questa organizzazione') : __('questo utente');
if (!$this->logged_user->canEditContactsForActor($actor, 'telegram_chat')) throw new ForbiddenException(__('Non hai i permessi per modificare i recapiti di questo tipo per {0}', $actor_type_description));
if ($this->request->is(['patch', 'post', 'put'])) {
$telegram_chat = $this->TelegramChats->patchEntity($telegram_chat, $this->request->getData());
if ($this->TelegramChats->save($telegram_chat)) {
$this->Flash->success(__('La Chat Telegram è stata modificata con successo.'));
return $this->redirect(['controller' => 'Deliveries', 'action' => 'index', $telegram_chat->delivery->actor_id]);
}
$this->Flash->error(__('Errore durante la modifica della Chat Telegram. Riprovare di nuovo'));
}
$this->set(compact('actor', 'telegram_chat'));
}
}

624
idrocap_wa/src/Controller/UsersController.php

@ -0,0 +1,624 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Event\EventInterface;
use Cake\View\JsonView;
use App\View\CustomCsvView;
use Cake\I18n\DateTime;
use Cake\Http\Exception\ForbiddenException;
use Cake\Http\Exception\NotFoundException;
use Cake\Core\Configure;
use App\WGS\Auth\OpenIdConnectClient;
/**
* Users Controller
*
* @property \App\Model\Table\UsersTable $Users
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class UsersController extends AppController
{
public function beforeFilter(EventInterface $event)
{
$this->Authentication->allowUnauthenticated(['logout', 'login', 'login_oidc', 'login_oidc_authenticated', 'passwordRecovery', 'chooseNewPassword', 'oneTimePassword', 'add_citizen', 'verify_citizen']);
}
public function viewClasses(): array
{
return [JsonView::class, CustomCsvView::class];
}
public function login_oidc()
{
OpenIdConnectClient::authorize(
Configure::read('App.oidc.authUrl'),
Configure::read('App.oidc.clientId'),
Configure::read('App.oidc.scopes'),
Configure::read('App.oidc.redirectUri')
);
}
public function login_oidc_authenticated()
{
try {
$access_token = OpenIdConnectClient::get_access_token(
Configure::read('App.oidc.tokenUrl'),
Configure::read('App.oidc.issuer'),
Configure::read('App.oidc.clientId'),
Configure::read('App.oidc.clientSecret'),
Configure::read('App.oidc.redirectUri')
);
$userinfo = OpenIdConnectClient::get_user_info(Configure::read('App.oidc.userinfoUrl'), Configure::read('App.oidc.userinfo_claim_key'), $access_token);
$userinfo_claim_value = $userinfo[Configure::read('App.oidc.userinfo_claim_key')];
$user = $this->Users->find()->where(['Users.' . Configure::read('App.oidc.usermodel_matching_attribute') => $userinfo_claim_value]);
if ($user->count() > 1) throw new \Exception(__('Non è stato possibile identificare in maniera univoca l\'utente in base all\'attributo \' {0} \' ricevuto dal provider', Configure::read('App.oidc.userinfo_claim_key')));
$user = $user->first();
$isUserVerified = $user?->is_verified ?? false;
if ($user && $isUserVerified) {
$this->Authentication->setIdentity($user);
} else {
if (!$user) {
$citizenRegistrationDTO = (new \App\WGS\Utils\Dto\Users\CitizenRegistrationDTO())
->setShowAlert(true)
// attenzione!!!!!! se $userinfo_claim_value NON COINCIDE CON IL CF, NON FUNZIONA + !!!!!!!!!!!!!!!!!!!!!!!!!!
->setFiscalCode($userinfo[Configure::read('App.oidc.userinfo_claim_key')] ?? '')
->setName($userinfo['given_name'] ?? '')
->setSurname($userinfo['family_name'] ?? '')
->setGender($userinfo['gender'] ?? '')
->setBirthdate($userinfo['birthdate'] ?? '');
$this->request->getSession()->write('showCitizenRegistration', $citizenRegistrationDTO);
$errorMessage = __('Errore durante l\'autenticazione mediante {0} "Utente non trovato". Riprovare di nuovo o utilizzare le credenziali di accesso', Configure::read('App.oidc.idp_name', 'OpenID Connect'));
} else if ($user && !$isUserVerified) {
$errorMessage = __('Utente non verificato. Controlla la tua email per completare la verifica.');
}
$this->Flash->error($errorMessage);
}
return $this->redirect('/');
} catch (\Exception $e) {
$this->Flash->error(__('Errore durante l\'autenticazione mediante {0} "{1}". Riprovare di nuovo o utilizzare le credenziali di accesso', Configure::read('App.oidc.idp_name', 'OpenID Connect'), $e->getMessage()));
return $this->redirect('/');
}
}
/**
* index
*
* @throws ForbiddenException
* @return \Cake\Http\Response
*/
public function index()
{
if (!$this->logged_user->hasCapability(['configuration.users.read', 'configuration.users.read_foo'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$users = $this->Users->find()
->contain(['Actors', 'Organisations' => ['Actors', 'OrganisationActors']]);
if (!$this->logged_user->hasCapability('configuration.users.read')) {
$users = $users->where(['Users.organisation_id' => $this->logged_user->organisation_id]);
}
$this->set('total_users', $users->count());
// applichiamo gli eventuali filtri presenti:
$users = $this->applyFilters($users);
$this->set('filtered_users', $users->count());
if (!$this->getRequest()->is(['json', 'xml', 'csv'])) {
$users = $users
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
return $results->map(function ($row) {
$row->can_edit = $this->logged_user->id == $row->id || ($this->logged_user->organisation_id == $row->organisation_id && $this->logged_user->hasCapability('configuration.users.edit_foo')) || $this->logged_user->hasCapability('configuration.users.edit');
$row->can_delete = $this->logged_user->id != $row->id && (($this->logged_user->organisation_id == $row->organisation_id && $this->logged_user->hasCapability('configuration.users.delete_foo')) || $this->logged_user->hasCapability('configuration.users.delete'));
return $row;
});
});
}
$this->paginate = [
'sortableFields' => ['surname', 'name', 'username', 'OrganisationActors.description'],
];
$this->set('users', $this->getRequest()->is(['json', 'xml', 'csv']) ? $users : $this->paginate($users));
if ($this->getRequest()->is('csv')) {
if (!$this->logged_user->hasCapability('configuration.users.export_csv')) throw new ForbiddenException(__('Non hai i permessi necessari'));
// docs here: https://github.com/FriendsOfCake/cakephp-csvview
$header = [
__('Cognome'),
__('Nome'),
__('Username'),
__('Recapiti'),
__('Profili'),
__('Organizzazione'),
];
$extract = [
'surname',
'name',
'username',
function (array $row) {
return $row['actor']['contacts'];
},
'profiles',
function (array $row) {
return $row['organisation']['organisation_actor']['description'];
},
];
$this->viewBuilder()
->setClassName(CustomCsvView::class)
->setOptions([
'header' => $header,
'extract' => $extract,
]);
$dateTime = DateTime::now();
$timestamp = $dateTime->i18nFormat('Y-M-d-H-m-ss', $this->logged_user->timezone);
$this->setResponse($this->getResponse()->withDownload("users_$timestamp.csv"));
}
$this->viewBuilder()->setOption('serialize', 'users');
}
/**
* view
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function view($id = null)
{
if ($this->logged_user->id != $id && !$this->logged_user->hasCapability(['configuration.users.read', 'configuration.users.read_foo'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$user = $this->Users->get($id, contain: ['Actors', 'Languages', 'Organisations' => ['Actors']]);
if (!$this->logged_user->hasCapability('configuration.users.read') && $this->logged_user->organisation_id != $user->organisation_id && $this->logged_user->id != $user->id) throw new ForbiddenException(__('Non hai i permessi necessari'));
$user->can_handle_contacts = $this->logged_user->canViewContactsForActor($this->Users->Actors->get($user->actor_id, contain: ['Users']));
$user->can_edit = $this->logged_user->hasCapability('configuration.users.edit') || $this->logged_user->organisation_id == $user->organisation_id && $this->logged_user->hasCapability('configuration.users.edit_foo') || $this->logged_user->id == $user->id;
$user->can_delete = $this->logged_user->id != $user->id && (($this->logged_user->organisation_id == $user->organisation_id && $this->logged_user->hasCapability('configuration.users.delete_foo')) || $this->logged_user->hasCapability('configuration.users.delete'));
$this->set(compact('user'));
$this->viewBuilder()->setOption('serialize', 'user');
}
/**
* add
*
* @return \Cake\Http\Response
*/
public function add()
{
if (!$this->logged_user->hasCapability(['configuration.users.add', 'configuration.users.add_foo'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$can_view_profiles = $this->logged_user->hasCapability('configuration.groups.read');
$can_handle_profiles = $can_view_profiles && ($this->logged_user->hasCapability('configuration.users.profiles') || $this->fetchTable('Users')->getAssociableUserGroups()->count() > 0);
$user = $this->Users->newEmptyEntity();
$default_profiles = $this->Users->Groups->find()
->contain(['Actors'])
->where(['Groups.is_default' => 1])
->all()
->toArray();
$user['groups'] = $default_profiles;
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->getData());
if (!$can_handle_profiles) $user['groups'] = $default_profiles;
if ($this->Users->save($user)) {
$this->Flash->success(__('Utente creato con successo.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Errore durante la creazione dell\'utente'));
}
$organisations = $this->Users->Organisations->find('list')->contain(['Actors'])->order(['Actors.description' => 'ASC']);
if (!$this->logged_user->hasCapability('configuration.users.add')) $organisations = $organisations->where(['Organisations.id' => $this->logged_user->organisation_id]);
$languages = $this->Users->Languages->find('list');
$groups = $this->fetchTable('Users')->getAssociableUserGroups(as_list: true)->order(['Groups.description' => 'ASC']);
$this->set('today', DateTime::now());
$this->set(compact('user', 'organisations', 'languages', 'groups', 'can_view_profiles', 'can_handle_profiles'));
}
/**
* edit
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function edit($id = null)
{
if ($this->logged_user->id != $id && !$this->logged_user->hasCapability(['configuration.users.edit', 'configuration.users.edit_foo'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$can_view_profiles = $this->logged_user->hasCapability('configuration.groups.read');
$can_handle_profiles = $can_view_profiles && ($this->logged_user->hasCapability('configuration.users.profiles') || $this->fetchTable('Users')->getAssociableUserGroups()->count() > 0);
$user = $this->Users->get($id, contain: ['Actors', 'Groups', 'Languages', 'Organisations' => ['Actors']]);
if ($this->request->is(['patch', 'post', 'put'])) {
$user->setAccess('groups', $can_handle_profiles);
$user = $this->Users->patchEntity($user, $this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success(__('Utente "{0}" modificato con successo.', $user->actor->description));
return $this->logged_user->hasCapability(['configuration.users.read', 'configuration.users.read_foo']) ? $this->redirect(['action' => 'index']) : $this->redirect('/');
}
$this->Flash->error(__('Errore durante la modifica dell\'utente "{0}". Riprovare di nuovo.', $user->actor->description));
}
$organisations = $this->Users->Organisations->find('list')->contain(['Actors'])->order(['Actors.description' => 'ASC']);
if (!$this->logged_user->hasCapability('configuration.users.edit')) $organisations = $organisations->where(['Organisations.id' => $this->logged_user->organisation_id]);
$languages = $this->Users->Languages->find('list');
$groups = $this->fetchTable('Users')->getAssociableUserGroups(as_list: true)->order(['Groups.description' => 'ASC']);
$this->set('today', DateTime::now());
$this->set(compact('user', 'organisations', 'languages', 'groups', 'can_view_profiles', 'can_handle_profiles'));
}
/**
* delete
*
* @param Int $id
* @return \Cake\Http\Response
*/
public function delete($id = null)
{
if (!$this->logged_user->hasCapability(['configuration.users.delete', 'configuration.users.delete_foo'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$this->request->allowMethod(['post', 'delete']);
$user = $this->Users->get($id, contain: ['Actors']);
if ($this->logged_user->id == $user->id || ($this->logged_user->organisation_id != $user->organisation_id && !$this->logged_user->hasCapability('configuration.users.delete'))) throw new ForbiddenException(__('Non hai i permessi necessari'));
if ($this->Users->Actors->delete($user->actor)) {
$this->Flash->success(__('L\'utente "{0}" è stato cancellato con successo', $user->actor->description));
} else {
$this->Flash->error(__('Errore durante la cancellazione dell\'utente "{0}". Riprovare di nuovo.', $user->actor->description));
}
return $this->redirect(['action' => 'index']);
}
/**
* login
*
* @return \Cake\Http\Response
*/
public function login()
{
$result = $this->Authentication->getResult();
// If the user is logged in send them away.
if ($result->isValid()) {
$user = $this->Authentication->getIdentity();
$user->timezone = $this->request->getData('user_timezone', Configure::read('App.defaultUserTimezone'));
// se per qualche motivo il form restituisce "" stringa vuota per il timezone, impostiamo invece il default!
if (empty($user->timezone)) $user->timezone = Configure::read('App.defaultUserTimezone');
$this->fetchTable('Users')->save($user);
$target = $this->Authentication->getLoginRedirect() ?? '/';
$this->request->getSession()->write("oidc", null);
return $this->redirect($target);
}
if ($this->request->is('post')) {
$authError = $this->request->getSession()->read('AuthError');
if ($authError) {
$this->Flash->error($authError);
$this->request->getSession()->delete('AuthError');
} else {
$this->Flash->error('username e/o password errati');
}
}
$login_citizen_registration = $this->request->getSession()->read('showCitizenRegistration');
$this->set(compact('login_citizen_registration'));
$this->viewBuilder()->setLayout('CakeLte.login');
$this->set('login_layout_link_label', __('Ho dimenticato la mia password'));
$this->set('login_layout_link_action', 'passwordRecovery');
$this->set('login_layout_privacy_label', __('Visualizza Privacy Policy'));
$this->set('login_layout_privacy_link', 'view_public');
if (isset($login_citizen_registration) && $login_citizen_registration->isShowAlert()) {
$login_citizen_registration_copy = clone $login_citizen_registration;
$login_citizen_registration_copy->setShowAlert(false);
$this->request->getSession()->write('showCitizenRegistration', $login_citizen_registration_copy);
}
}
/**
* logout
*
* @return \Cake\Http\Response
*/
public function logout()
{
$this->Authentication->logout();
if ($this->request->getSession()->read('oidc')) {
OpenIdConnectClient::logout(
Configure::read('App.oidc.logoutUrl'),
Configure::read('App.oidc.clientId'),
Configure::read('App.oidc.postLogoutRedirectUri')
);
}
$this->Flash->success(__('Logout eseguito con successo.'));
return $this->redirect(['controller' => 'Users', 'action' => 'login']);
}
/**
* executePasswordRecovery
*
* @param \App\Model\Entity\User $user
* @param String $email
* @param String|Null $password_recovery_token
* @return String|Null
*/
private function executePasswordRecovery(\App\Model\Entity\User $user, String $email, String|Null $password_recovery_token = null): String|Null
{
$user->password_recovery_token = $password_recovery_token ?? sha1($this->gen_random_code(40));
if (!$this->Users->save($user)) return null;
$options = [];
$options['user_id'] = $user->id;
$options['username'] = $user->username;
$options['email'] = $email;
$options['type'] = 'users';
$options['password_recovery_token'] = $user->password_recovery_token;
$this->sendNotifications(null, 'password_recovery', $options);
return $user->password_recovery_token;
}
/**
* passwordRecovery
*
* @return \Cake\Http\Response
*/
public function passwordRecovery()
{
if ($this->request->is('post'))
{
if (empty($this->request->getData('email'))) {
$this->Flash->error(__('Inserire un indirizzo email valido!'));
return $this->redirect(['action' => 'passwordRecovery']);
}
$email = $this->Users->Actors->Deliveries->Emails->find()
->contain(['Deliveries' => ['Actors' => ['Users']]])
->where(['Emails.value' => $this->request->getData('email')])
->first();
if (isset($email->delivery->actor->user))
{
$this->executePasswordRecovery($email->delivery->actor->user, $this->request->getData('email'));
$this->Flash->success(__('A breve riceverai un\'email all\'indirizzo "{0}" con le istruzioni per eseguire il reset della password per il tuo account con username "{1}".', $this->request->getData('email'), $email->delivery->actor->user->username));
return $this->redirect(['action' => 'login']);
}
else
{
$this->Flash->error(__('L\'indirizzo email inserito non risulta presente'));
return $this->redirect(['action' => 'passwordRecovery']);
}
}
$this->viewBuilder()->setLayout('CakeLte.login');
$this->set('login_layout_link_label', __('Ritorna al login'));
$this->set('login_layout_link_action', 'login');
$this->set('login_layout_privacy_label', __('Visualizza Privacy Policy'));
$this->set('login_layout_privacy_link', 'view_public');
}
/**
* chooseNewPassword
*
* @param String $token
* @return \Cake\Http\Response
*/
public function chooseNewPassword($token)
{
$user = $this->Users->find()->contain(['Actors', 'Organisations'])->where(['Users.password_recovery_token' => $token])->first();
if(!$user)
{
$this->Flash->error(__('Impossibile completare la procedura di recupero password.'));
return $this->redirect(['action' => 'login']);
}
if ($this->request->is('post'))
{
if (!empty($this->request->getData('new_password')) && !empty($this->request->getData('confirm_new_password')) && $this->request->getData('new_password') == $this->request->getData('confirm_new_password')) {
$user_data = $user->toArray();
$user_data['password'] = $this->request->getData('new_password');
$user = $this->Users->patchEntity($user, $user_data, ['validate' => false]);
if (count($user->getError('password')) > 0) {
$this->Flash->error(implode(" - ", $user->getError('password')));
return $this->redirect(['action' => 'chooseNewPassword', $token]);
}
$user->password_recovery_token = null;
$user->last_change_password = new \DateTime();
$user->password_recovery_counter = $user->password_recovery_counter+1;
if ($this->Users->save($user)) {
$this->Flash->success('Password modificata con successo');
return $this->redirect(['action' => 'login']);
} else {
$this->Flash->error(__('Impossibile cambiare la password'));
}
} else {
$this->Flash->error(__('La nuova password non è valida e/o le 2 password non coincidono'));
}
}
$this->viewBuilder()->setLayout('CakeLte.login');
$this->set('login_layout_link_label', __('Ritorna al login'));
$this->set('login_layout_link_action', 'login');
$this->set('login_layout_privacy_label', __('Visualizza Privacy Policy'));
$this->set('login_layout_privacy_link', 'view_public');
}
public function oneTimePassword(String $otp) {
$users = $this->fetchTable('Users')->find()->where(['Users.otp' => $otp]);
if ($users->count() != 1) {
$this->Flash->error(__('Codice OTP non valido o già utilizzato. Effettua il login.'));
return $this->redirect(['action' => 'login']);
}
$user = $users->first();
// controllo che otp_expires sia un datetime valido e non scaduto:
if ($user->otp_expires === null || !is_object($user->otp_expires) || get_class($user->otp_expires) != "Cake\I18n\DateTime" || !$user->otp_expires->isWithinNext(Configure::read('App.oneTimePasswordExpirationHours', 1) . ' hours')) {
$user->otp = null;
$user->otp_redirect_url = null;
$user->otp_expires = null;
$this->fetchTable('Users')->save($user);
$this->Flash->error(__('Codice OTP scaduto. Effettua il login.'));
return $this->redirect(['action' => 'login']);
}
if ($this->request->is('post'))
{
$redirect_url = $user->otp_redirect_url ?? '/';
$user->sys_admin = false;
$user->passepartout = false;
$user->otp = null;
$user->otp_redirect_url = null;
$user->otp_expires = null;
$this->fetchTable('Users')->save($user);
$this->getRequest()->getSession()->write('Auth', $user);
return $this->redirect($redirect_url);
}
$this->viewBuilder()->setLayout('CakeLte.login');
$this->set('login_layout_link_label', __('Ritorna al login'));
$this->set('login_layout_link_action', 'login');
$this->set('login_layout_privacy_label', __('Visualizza Privacy Policy'));
$this->set('login_layout_privacy_link', 'view_public');
}
/**
* getPhoto
*
* @param String $file_name
* @return \Cake\Http\Response
*/
public function getPhoto($file_name)
{
$photo_owner = $this->fetchTable('Users')->find()->where(['Users.photo' => $file_name])->first();
if (!$photo_owner) throw new NotFoundException(__('Foto utente non trovata!'));
if ($this->logged_user->id != $photo_owner->id) {
if (!$this->logged_user->hasCapability(['configuration.users.read', 'configuration.users.read_foo'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
if (!$this->logged_user->hasCapability(['configuration.users.read']) && $this->logged_user->organisation_id != $photo_owner->organisation_id) throw new ForbiddenException(__('Non hai i permessi necessari'));
}
$mimetype = "image/jpeg";
$path = Configure::read('App.userPhotoPath') . "/" . $file_name;
$output = \App\WGS\FileStorage\FileStorageFactory::create()->getFile($path);
$this->response = $this->response
->withType($mimetype)
->withStringBody($output);
return $this->response;
}
/**
* deleteUserPhoto
*
* @param Int $user_id
* @return \Cake\Http\Response
*/
public function deleteUserPhoto($user_id)
{
$user = $this->Users->get($user_id);
$user->photo = null;
if (!$this->Users->save($user)) {
$this->Flash->error(__('Errore durante l\'eliminazione della foto utente!'));
} else {
$this->Flash->success(__('Foto utente eliminata con successo'));
}
return $this->redirect(['action' => 'view', $user_id]);
}
/**
* add_citizen
* @return \Cake\Http\Response
*/
public function add_citizen()
{
$user = $this->Users->newEmptyEntity();
$citizenRegistrationData = $this->request->getSession()->read('showCitizenRegistration');
if ($this->request->is('post') || $this->request->is('put')) {
$data = $this->request->getData();
$taxCode = $data['tax_code'] ?? null;
$name = $data['name'] ?? null;
$surname = $data['surname'] ?? null;
$birthday = $data['birthday'] ?? null;
$gender = $data['gender'] ?? null;
if ($taxCode && $citizenRegistrationData && $citizenRegistrationData?->getFiscalCode() !== '' && $citizenRegistrationData->getFiscalCode() != $taxCode) {
$this->Flash->error(__('Il codice fiscale inserito non corrisponde a quello fornito in fase di autenticazione. Riprovare.'));
}
elseif ($name && $citizenRegistrationData && $citizenRegistrationData?->getName() !== '' && $citizenRegistrationData->getName() != $name) {
$this->Flash->error(__('Il nome inserito non corrisponde a quello fornito in fase di autenticazione. Riprovare.'));
}
elseif ($surname && $citizenRegistrationData && $citizenRegistrationData?->getSurname() !== '' && $citizenRegistrationData->getSurname() != $surname) {
$this->Flash->error(__('Il cognome inserito non corrisponde a quello fornito in fase di autenticazione. Riprovare.'));
}
elseif ($birthday && $citizenRegistrationData && $citizenRegistrationData?->getBirthdate() !== '' && $citizenRegistrationData->getBirthdate() != $birthday) {
$this->Flash->error(__('La data di nascita inserita non corrisponde a quella fornita in fase di autenticazione. Riprovare.'));
}
elseif ($gender && $citizenRegistrationData && $citizenRegistrationData?->getGender() !== '' && $citizenRegistrationData->getGender() != $gender) {
$this->Flash->error(__('Il sesso inserito non corrisponde a quello fornito in fase di autenticazione. Riprovare.'));
}
else {
$newCitizen = $this->Users->createCitizen($user, $data);
if ($newCitizen) {
$emails = $newCitizen?->getEmailsWithNotifications() ?? [];
$notificationEmail = $emails[0] ?? null;
$successMessage = 'Utente creato con successo.';
if ($notificationEmail) {
$options = [
'user_id' => $newCitizen->id,
'username' => $newCitizen->username,
'email' => $notificationEmail->value,
'type' => 'users',
'verification_token' => $newCitizen->email_verification_code,
];
$this->sendNotifications(null, 'verify-citizen-registration', $options);
$successMessage = 'Utente creato con successo. Riceverai un\'email per confermare la registrazione.';
}
$this->request->getSession()->delete('showCitizenRegistration');
$this->Flash->success(__($successMessage));
return $this->redirect(['action' => 'login']);
} else {
$this->Flash->error(__('Errore durante la creazione dell\'utente'));
}
}
}
$languages = $this->Users->Languages->find('list');
$this->viewBuilder()->setLayout('CakeLte.login');
$this->set([
'today' => DateTime::now(),
'hideLogin' => true,
'user' => $user,
'languages' => $languages,
'citizenRegistrationData' => $citizenRegistrationData,
]);
}
/**
* verify citizen
* @param string|null $token
* @return \Cake\Http\Response
*/
public function verify_citizen(string|null $token)
{
$token = $token ?? '';
if ($this->Users->verifyCitizen($token)) {
$this->Flash->success(__('Utente verificato con successo.'));
return $this->redirect(['action' => 'login']);
}
$this->Flash->error(__('Errore durante la verifica, si prega di riprovare.'));
return $this->redirect(['action' => 'login']);
}
}

124
idrocap_wa/src/Controller/WaterDrawingArticlesController.php

@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
namespace App\Controller;
/**
* WaterDrawingArticles Controller
*
* @property \App\Model\Table\WaterDrawingArticlesTable $WaterDrawingArticles
* @method \App\Model\Entity\WaterDrawingArticle[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class WaterDrawingArticlesController extends AppController
{
/**
* Index method
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function index()
{
$waterDrawingArticles = $this->WaterDrawingArticles->find()->formatResults(function (\Cake\Collection\CollectionInterface $results) {
return $results->map(function ($row) {
$row->can_edit = $this->logged_user->hasCapability('configuration.water_drawing_articles.edit');
$row->can_delete = $this->logged_user->hasCapability('configuration.water_drawing_articles.delete');
$row->can_disable = $this->logged_user->hasCapability('configuration.water_drawing_articles.disable');
return $row;
});
});
$waterDrawingArticles = $this->paginate($waterDrawingArticles);
$this->set(compact('waterDrawingArticles'));
}
/**
* View method
*
* @param string|null $id Water Drawing Article id.
* @return \Cake\Http\Response|null|void Renders view
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($id = null)
{
$waterDrawingArticle = $this->WaterDrawingArticles->get($id, contain: []);
$waterDrawingArticle->can_edit = $this->logged_user->hasCapability('configuration.water_drawing_articles.edit');
$waterDrawingArticle->can_delete = $this->logged_user->hasCapability('configuration.water_drawing_articles.delete');
$waterDrawingArticle->can_disable = $this->logged_user->hasCapability('configuration.water_drawing_articles.disable');
$this->set(compact('waterDrawingArticle'));
}
/**
* Add method
*
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
*/
public function add()
{
$waterDrawingArticle = $this->WaterDrawingArticles->newEmptyEntity();
if ($this->request->is('post')) {
$waterDrawingArticle = $this->WaterDrawingArticles->patchEntity($waterDrawingArticle, $this->request->getData());
if ($this->WaterDrawingArticles->save($waterDrawingArticle)) {
$this->Flash->success(__('Articolo di legge salvato con successo.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Errore durante il salvataggio dell\'articolo di legge. Riprovare di nuovo.'));
}
$this->set(compact('waterDrawingArticle'));
}
/**
* Edit method
*
* @param string|null $id Water Drawing Article id.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit($id = null)
{
$waterDrawingArticle = $this->WaterDrawingArticles->get($id, contain: []);
if ($this->request->is(['patch', 'post', 'put'])) {
$waterDrawingArticle = $this->WaterDrawingArticles->patchEntity($waterDrawingArticle, $this->request->getData());
if ($this->WaterDrawingArticles->save($waterDrawingArticle)) {
$this->Flash->success(__('Articolo di legge modificato con successo.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Errore durante la modifica dell\'articolo di legge. Riprovare di nuovo.'));
}
$this->set(compact('waterDrawingArticle'));
}
/**
* Delete method
*
* @param string|null $id Water Drawing Article id.
* @return \Cake\Http\Response|null|void Redirects to index.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function delete($id = null)
{
$this->request->allowMethod(['post', 'delete']);
$waterDrawingArticle = $this->WaterDrawingArticles->get($id);
if ($this->WaterDrawingArticles->delete($waterDrawingArticle)) {
$this->Flash->success(__('L\'articolo di legge è stato cancellato con successo.'));
} else {
$this->Flash->error(__('Errore durante la cancellazione dell\'articolo di legge. Riprovare di nuovo.'));
}
return $this->redirect(['action' => 'index']);
}
public function change_status($id){
$waterDrawingArticle = $this->WaterDrawingArticles->get($id);
$waterDrawingArticle = $this->WaterDrawingArticles->patchEntity($waterDrawingArticle, [
'disable' => $waterDrawingArticle->disable ? false : true
]);
if($this->WaterDrawingArticles->save($waterDrawingArticle)){
$this->Flash->success( $waterDrawingArticle->disable ? __('Articolo disabilitato con successo.') : __('Articolo abilitato con successo.'));
}else{
$this->Flash->error( $waterDrawingArticle->disable ? __('Errore durante l\'abilitazione dell\'articolo.') : __('Errore durante la disabilitazione dell\'articolo.'));
}
return $this->redirect(['action' => 'index']);
}
}

123
idrocap_wa/src/Controller/WaterDrawingDerivationsController.php

@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\WGS\SnapshotsHandler\SnapshotsHandler;
/**
* WaterDrawingDerivations Controller
*
* @property \App\Model\Table\WaterDrawingDerivationsTable $WaterDrawingDerivations
* @method \App\Model\Entity\WaterDrawingDerivation[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class WaterDrawingDerivationsController extends AppController
{
/**
* Index method
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function index()
{
$this->paginate = [
'contain' => ['WaterDrawingDerivationTypes', 'WaterDrawingPaperworks'],
];
$waterDrawingDerivations = $this->paginate($this->WaterDrawingDerivations);
$this->set(compact('waterDrawingDerivations'));
}
/**
* View method
*
* @param string|null $id Water Drawing Derivation id.
* @return \Cake\Http\Response|null|void Renders view
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($id = null)
{
$waterDrawingDerivation = $this->WaterDrawingDerivations->find()
->contain(['WaterDrawingDerivationTypes', 'WaterDrawingPaperworks'])
->where(['WaterDrawingDerivations.id' => $id])
->formatResults(function (\Cake\Collection\CollectionInterface $results){
return $results->map(function ($row){
$row->can_edit = true;
$row->can_delete = true;
return $row;
});
})->first();
$this->set(compact('waterDrawingDerivation'));
}
/**
* Add method
*
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
*/
public function add($water_drawing_paperwork_id)
{
$waterDrawingDerivation = $this->WaterDrawingDerivations->newEmptyEntity();
if ($this->request->is('post')) {
$waterDrawingDerivation = $this->WaterDrawingDerivations->patchEntity($waterDrawingDerivation, $this->request->getData());
if ($this->WaterDrawingDerivations->save($waterDrawingDerivation)) {
$waterDrawingPaperworks = $this->WaterDrawingDerivations->WaterDrawingPaperworks->get($water_drawing_paperwork_id);
SnapshotsHandler::createSnapshot($waterDrawingPaperworks->controllable_object_id);
$this->Flash->success(__('Il punto di prelievo è stato salvato con successo'));
return $this->redirect(['controller' => 'WaterDrawingPaperworks', 'action' => 'view', $waterDrawingDerivation->water_drawing_paperwork_id]);
}
$this->Flash->error(__('Errore durante il salvataggio del punto di prelievo.'));
}
$waterDrawingDerivationTypes = $this->WaterDrawingDerivations->WaterDrawingDerivationTypes->find()->all()->combine('id','description')->toArray();
$this->set(compact('waterDrawingDerivation', 'waterDrawingDerivationTypes', 'water_drawing_paperwork_id'));
}
/**
* Edit method
*
* @param string|null $id Water Drawing Derivation id.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit($id = null)
{
$waterDrawingDerivation = $this->WaterDrawingDerivations->get($id, contain: ['WaterDrawingPaperworks']);
if ($this->request->is(['patch', 'post', 'put'])) {
$waterDrawingDerivation = $this->WaterDrawingDerivations->patchEntity($waterDrawingDerivation, $this->request->getData());
if ($this->WaterDrawingDerivations->save($waterDrawingDerivation)) {
SnapshotsHandler::createSnapshot($waterDrawingDerivation->water_drawing_paperwork->controllable_object_id);
$this->Flash->success(__('Il punto di prelievo è stato salvato con successo'));
return $this->redirect(['controller' => 'WaterDrawingPaperworks', 'action' => 'view', $waterDrawingDerivation->water_drawing_paperwork_id]);
}
$this->Flash->error(__('Errore durante il salvataggio del punto di prelievo.'));
}
$waterDrawingDerivationTypes = $this->WaterDrawingDerivations->WaterDrawingDerivationTypes->find()->all()->combine('id', 'description')->toArray();
$this->set(compact('waterDrawingDerivation', 'waterDrawingDerivationTypes'));
}
/**
* Delete method
*
* @param string|null $id Water Drawing Derivation id.
* @return \Cake\Http\Response|null|void Redirects to index.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function delete($id = null)
{
$this->request->allowMethod(['post', 'delete']);
$waterDrawingDerivation = $this->WaterDrawingDerivations->get($id, contain: ['WaterDrawingPaperworks']);
if ($this->WaterDrawingDerivations->delete($waterDrawingDerivation)) {
SnapshotsHandler::createSnapshot($waterDrawingDerivation->water_drawing_paperwork->controllable_object_id);
$this->Flash->success(__('Il punto di prelievo è stato cancellato'));
return $this->redirect(['controller' => 'WaterDrawingPaperworks', 'action' => 'view', $waterDrawingDerivation->water_drawing_paperwork_id]);
} else {
$this->Flash->error(__('Errore durante la cancellazione del punto di prelievo.'));
}
return $this->redirect(['action' => 'view', $waterDrawingDerivation->id]);
}
}

174
idrocap_wa/src/Controller/WaterDrawingFeesController.php

@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\WGS\SnapshotsHandler\SnapshotsHandler;
use Cake\Http\Exception\ForbiddenException;
use Cake\I18n\DateTime;
use Cake\View\JsonView;
use App\View\CustomCsvView;
/**
* WaterDrawingFees Controller
*
* @property \App\Model\Table\WaterDrawingFeesTable $WaterDrawingFees
* @method \App\Model\Entity\WaterDrawingFee[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class WaterDrawingFeesController extends AppController
{
public function viewClasses(): array
{
return [JsonView::class, CustomCsvView::class];
}
/**
* Index method
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function index($water_drawing_paperwork_id)
{
if (!$this->logged_user->hasCapability(['documentation.water_drawing_fees.view'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$waterDrawingFees = $this->WaterDrawingFees->find()->contain(['WaterDrawingPayments'])->where(['WaterDrawingFees.water_drawing_paperwork_id' => $water_drawing_paperwork_id])->formatResults(function ($result){
return $result->map(function ($row){
$row->can_edit = $this->logged_user->hasCapability('documentation.water_drawing_fees.edit');
$row->can_delete = $this->logged_user->hasCapability('documentation.water_drawing_fees.delete') && (count($row->water_drawing_payments) == 0);
return $row;
});
});
$this->set('total_water_drawing_fees', $waterDrawingFees->count());
// applichiamo gli eventuali filtri presenti:
$waterDrawingFees = $this->applyFilters($waterDrawingFees);
$this->set('filtered_water_drawing_fees', $waterDrawingFees->count());
$waterDrawingFees = $this->paginate($waterDrawingFees);
$this->set(compact('waterDrawingFees', 'water_drawing_paperwork_id'));
$this->set('can_export_csv', $this->logged_user->hasCapability('documentation.water_drawing_paperworks.export_csv_payment'));
if ($this->getRequest()->is('csv')) {
if (!$this->logged_user->hasCapability('documentation.water_drawing_paperworks.export_csv_payment')) throw new ForbiddenException(__('Non hai i permessi necessari'));
// docs here: https://github.com/FriendsOfCake/cakephp-csvview
$header = [
__('Anno'),
__('Canone'),
__('Dovuto')
];
$extract = [
'year',
'amount',
'to_pay'
];
$this->viewBuilder()
->setClassName(CustomCsvView::class)
->setOptions([
'header' => $header,
'extract' => $extract,
]);
$dateTime = DateTime::now();
$timestamp = $dateTime->i18nFormat('Y-M-d-H-m-ss', $this->logged_user->timezone);
$this->setResponse($this->getResponse()->withDownload("water_drawing_fees_$water_drawing_paperwork_id-$timestamp.csv"));
}
$this->viewBuilder()->setOption('serialize', 'waterDrawingFees');
}
/**
* View method
*
* @param string|null $id Water Drawing Fee id.
* @return \Cake\Http\Response|null|void Renders view
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($id = null)
{
if (!$this->logged_user->hasCapability(['documentation.water_drawing_fees.view'])) throw new ForbiddenException(__('Non hai i permessi necessari'));
$waterDrawingFee = $this->WaterDrawingFees->find()->contain(['WaterDrawingPayments'])->where(['WaterDrawingFees.id' => $id])->formatResults(function ($result){
return $result->map(function ($row){
$row->can_edit = $this->logged_user->hasCapability('documentation.water_drawing_fees.edit');
$row->can_delete = $this->logged_user->hasCapability('documentation.water_drawing_fees.delete') && (count($row->water_drawing_payments) == 0);
return $row;
});
})->first();
$waterDrawingPayments = $this->WaterDrawingFees->WaterDrawingPayments->find()->contain(['WaterDrawingFees', 'WaterDrawingPaymentTypes', 'Users'])->where(['WaterDrawingPayments.water_drawing_fee_id' => $id])->formatResults(function ($result){
return $result->map(function ($row){
$row->can_edit = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.add_payment');
$row->can_view = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.view_payment');
$row->can_delete = $this->logged_user->hasCapability('documentation.water_drawing_paperworks.delete_payment');
return $row;
});
});
$this->set(compact('waterDrawingFee', 'waterDrawingPayments'));
}
/**
* Add method
*
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
*/
public function add($water_drawing_paperwork_id)
{
$waterDrawingFee = $this->WaterDrawingFees->newEmptyEntity();
if ($this->request->is('post')) {
$waterDrawingFee = $this->WaterDrawingFees->patchEntity($waterDrawingFee, $this->request->getData());
if ($this->WaterDrawingFees->save($waterDrawingFee)) {
$waterDrawingPaperworks = $this->WaterDrawingFees->WaterDrawingPaperworks->get($water_drawing_paperwork_id);
SnapshotsHandler::createSnapshot($waterDrawingPaperworks->controllable_object_id);
$this->Flash->success(__('Il canone annuale è stato salvato.'));
return $this->redirect(['controller' => 'WaterDrawingPaperworks', 'action' => 'view', $waterDrawingFee->water_drawing_paperwork_id]);
}
$this->Flash->error(__('Errore. Il canone annuale non è stato salvato.'));
}
$this->set(compact('waterDrawingFee', 'water_drawing_paperwork_id'));
}
/**
* Edit method
*
* @param string|null $id Water Drawing Fee id.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit($id = null)
{
$waterDrawingFee = $this->WaterDrawingFees->get($id);
if ($this->request->is(['patch', 'post', 'put'])) {
$waterDrawingFee = $this->WaterDrawingFees->patchEntity($waterDrawingFee, $this->request->getData());
if ($this->WaterDrawingFees->save($waterDrawingFee)) {
$this->Flash->success(__('Il canone annuale è stato salvato.'));
return $this->redirect(['controller' => 'WaterDrawingPaperworks', 'action' => 'view', $waterDrawingFee->water_drawing_paperwork_id]);
}
$this->Flash->error(__('Errore. Il canone annuale non è stato salvato.'));
}
$this->set(compact('waterDrawingFee'));
}
/**
* Delete method
*
* @param string|null $id Water Drawing Fee id.
* @return \Cake\Http\Response|null|void Redirects to index.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function delete($id = null)
{
$this->request->allowMethod(['post', 'delete']);
$waterDrawingFee = $this->WaterDrawingFees->get($id, contain: ['WaterDrawingPayments']);
if(count($waterDrawingFee->water_drawing_payments) > 0){
$this->Flash->error(__('Errore. Per poter eliminare il canone bisogna eliminare i pagamenti ad esso associati.'));
return $this->redirect(['action' => 'view', $id]);
}
if ($this->WaterDrawingFees->delete($waterDrawingFee)) {
$this->Flash->success(__('Il canone annuale è stato cancellato.'));
} else {
$this->Flash->error(__('Errore. Il canone annuale non è stato cancellato.'));
}
return $this->redirect(['controller' => 'WaterDrawingPaperworks', 'action' => 'view', $waterDrawingFee->water_drawing_paperwork_id]);
}
}

108
idrocap_wa/src/Controller/WaterDrawingIntendedUsesController.php

@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
namespace App\Controller;
/**
* WaterDrawingIntendedUses Controller
*
* @property \App\Model\Table\WaterDrawingIntendedUsesTable $WaterDrawingIntendedUses
* @method \App\Model\Entity\WaterDrawingIntendedUse[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class WaterDrawingIntendedUsesController extends AppController
{
/**
* Index method
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function index()
{
$this->paginate = [
'contain' => ['WaterDrawingPaperworks', 'WaterDrawingIntendedUseTypes'],
];
$waterDrawingIntendedUses = $this->paginate($this->WaterDrawingIntendedUses);
$this->set(compact('waterDrawingIntendedUses'));
}
/**
* View method
*
* @param string|null $id Water Drawing Intended Use id.
* @return \Cake\Http\Response|null|void Renders view
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($id = null)
{
$waterDrawingIntendedUse = $this->WaterDrawingIntendedUses->get($id, contain: ['WaterDrawingPaperworks', 'WaterDrawingIntendedUseTypes']);
$this->set(compact('waterDrawingIntendedUse'));
}
/**
* Add method
*
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
*/
public function add()
{
$waterDrawingIntendedUse = $this->WaterDrawingIntendedUses->newEmptyEntity();
if ($this->request->is('post')) {
$waterDrawingIntendedUse = $this->WaterDrawingIntendedUses->patchEntity($waterDrawingIntendedUse, $this->request->getData());
if ($this->WaterDrawingIntendedUses->save($waterDrawingIntendedUse)) {
$this->Flash->success(__('The water drawing intended use has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The water drawing intended use could not be saved. Please, try again.'));
}
$waterDrawingPaperworks = $this->WaterDrawingIntendedUses->WaterDrawingPaperworks->find('list')->all();
$waterDrawingIntendedUseTypes = $this->WaterDrawingIntendedUses->WaterDrawingIntendedUseTypes->find('list')->all();
$this->set(compact('waterDrawingIntendedUse', 'waterDrawingPaperworks', 'waterDrawingIntendedUseTypes'));
}
/**
* Edit method
*
* @param string|null $id Water Drawing Intended Use id.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit($id = null)
{
$waterDrawingIntendedUse = $this->WaterDrawingIntendedUses->get($id, contain: []);
if ($this->request->is(['patch', 'post', 'put'])) {
$waterDrawingIntendedUse = $this->WaterDrawingIntendedUses->patchEntity($waterDrawingIntendedUse, $this->request->getData());
if ($this->WaterDrawingIntendedUses->save($waterDrawingIntendedUse)) {
$this->Flash->success(__('The water drawing intended use has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The water drawing intended use could not be saved. Please, try again.'));
}
$waterDrawingPaperworks = $this->WaterDrawingIntendedUses->WaterDrawingPaperworks->find('list')->all();
$waterDrawingIntendedUseTypes = $this->WaterDrawingIntendedUses->WaterDrawingIntendedUseTypes->find('list')->all();
$this->set(compact('waterDrawingIntendedUse', 'waterDrawingPaperworks', 'waterDrawingIntendedUseTypes'));
}
/**
* Delete method
*
* @param string|null $id Water Drawing Intended Use id.
* @return \Cake\Http\Response|null|void Redirects to index.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function delete($id = null)
{
$this->request->allowMethod(['post', 'delete']);
$waterDrawingIntendedUse = $this->WaterDrawingIntendedUses->get($id);
if ($this->WaterDrawingIntendedUses->delete($waterDrawingIntendedUse)) {
$this->Flash->success(__('The water drawing intended use has been deleted.'));
} else {
$this->Flash->error(__('The water drawing intended use could not be deleted. Please, try again.'));
}
return $this->redirect(['action' => 'index']);
}
}

Some files were not shown because too many files have changed in this diff Show More

Caricamento…
Annulla
Salva