Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.12% covered (success)
95.12%
39 / 41
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
AppController
95.12% covered (success)
95.12%
39 / 41
75.00% covered (warning)
75.00%
3 / 4
19
0.00% covered (danger)
0.00%
0 / 1
 isAdminRequest
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 initialize
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 beforeFilter
92.86% covered (success)
92.86%
26 / 28
0.00% covered (danger)
0.00%
0 / 1
13.06
 beforeRender
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2declare(strict_types=1);
3
4namespace App\Controller;
5
6use App\Model\Entity\User;
7use App\Service\ConsentService;
8use App\Utility\I18nManager;
9use Cake\Controller\Controller;
10use Cake\Core\Configure;
11use Cake\Event\EventInterface;
12use Cake\Log\LogTrait;
13
14/**
15 * Application Controller
16 *
17 * Base controller for all application controllers.
18 *
19 * @property \Authentication\Controller\Component\AuthenticationComponent $Authentication
20 * @property \Cake\Controller\Component\FlashComponent $Flash
21 */
22class AppController extends Controller
23{
24    use LogTrait;
25
26    /**
27     * Store a cache key available for all controllers to use
28     * Created in the initialize method
29     *
30     * @var string
31     */
32    public string $cacheKey;
33
34    /**
35     * ConsentService for handling cookie consent and session management
36     *
37     * @var \App\Service\ConsentService
38     */
39    protected ConsentService $consentService;
40
41    /**
42     * Checks if the current request is an admin request.
43     *
44     * This method determines whether the request is intended for the admin section
45     * of the application by checking if the 'prefix' routing parameter is set to 'Admin'.
46     *
47     * @return bool Returns true if the request is for the admin section, false otherwise.
48     */
49    private function isAdminRequest(): bool
50    {
51        return $this->request->getParam('prefix') === 'Admin';
52    }
53
54    /**
55     * Initialization hook method.
56     *
57     * Use this method to add common initialization code like loading components.
58     *
59     * e.g. `$this->loadComponent('FormProtection');`
60     *
61     * @return void
62     */
63    public function initialize(): void
64    {
65        parent::initialize();
66        $this->cacheKey = hash('xxh3', json_encode($this->request->getAttribute('params')));
67        $this->loadComponent('Flash');
68        $this->loadComponent('Authentication.Authentication'); // Loads the component
69
70        // Initialize ConsentService
71        $this->consentService = new ConsentService();
72
73        // Only load FrontEndSite component for non-admin routes
74        if (!$this->request->getParam('prefix') || $this->request->getParam('prefix') !== 'Admin') {
75            $this->loadComponent('DefaultTheme.FrontEndSite');
76        }
77    }
78
79    /**
80     * beforeFilter callback.
81     *
82     * Executed before each controller action. Checks for admin access rights
83     * when accessing admin-prefixed routes.
84     *
85     * @param \Cake\Event\EventInterface $event The event instance.
86     * @return void
87     */
88    public function beforeFilter(EventInterface $event): void
89    {
90        parent::beforeFilter($event); // Call parent's beforeFilter
91
92        I18nManager::setLocaleForLanguage($this->request->getParam('lang', 'en'));
93
94        $identity = null;
95        // The AuthenticationComponent (loaded in initialize) makes the identity available.
96        // It relies on the AuthenticationMiddleware having run first to populate the request attribute.
97        if ($this->components()->has('Authentication')) {
98            $identity = $this->Authentication->getIdentity();
99        }
100
101        if ($identity) {
102            $originalData = $identity->getOriginalData();
103            if ($originalData instanceof User) {
104                $profilePic = $originalData->image_url ?? null;
105                // Only set profilePic if the user has an actual image file
106                if ($profilePic && $originalData->image) {
107                    $this->set(compact('profilePic'));
108                }
109            }
110        }
111
112        if ($this->isAdminRequest()) {
113            // If there is no identity, or the identifier part of the identity is null/empty
114            $isAdmin = false;
115            if ($identity) {
116                $originalData = $identity->getOriginalData();
117                if ($originalData instanceof User) {
118                    $isAdmin = (bool)$originalData->is_admin;
119                } elseif (is_array($originalData)) {
120                    $isAdmin = !empty($originalData['is_admin']);
121                }
122            }
123            if (!$identity || !$identity->getIdentifier() || !$isAdmin) {
124                $this->Flash->error(__('Access denied. You must be logged in as an admin to view this page.'));
125                $event->setResult($this->redirect(['_name' => 'home', 'prefix' => false]));
126
127                return;
128            }
129
130            I18nManager::setLocalForAdminArea();
131        }
132
133        // Handle consent data processing
134        $consentData = $this->consentService->getConsentData($this->request);
135        $this->set($consentData);
136
137        $this->set('activeCtl', $this->request->getParam('controller'));
138        $this->set('activeAct', $this->request->getParam('action'));
139    }
140
141    /**
142     * beforeRender method
143     *
144     * This method is called before the controller action is rendered. It
145     * sets the theme for the view based on the prefix of the request.
146     *
147     * @param \Cake\Event\EventInterface $event The event object.
148     * @return void
149     */
150    public function beforeRender(EventInterface $event): void
151    {
152        parent::beforeRender($event);
153
154        $theme = $this->request->getParam('prefix') === 'Admin'
155            ? Configure::read('Theme.admin_theme', 'AdminTheme')
156            : Configure::read('Theme.default_theme', 'DefaultTheme');
157
158        $this->viewBuilder()->setTheme($theme);
159    }
160}