Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

424 righe
19 KiB

<?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;
}
}