Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 96 |
|
0.00% |
0 / 19 |
CRAP | |
0.00% |
0 / 1 |
| Bootstrap | |
0.00% |
0 / 96 |
|
0.00% |
0 / 19 |
1482 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
20 | |||
| isStarted | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| isDispatched | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| isReady | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| start | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
| dispatchHttp | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
| displayMaintenancePage | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| dispatchCli | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
| dispatch | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
| catchError | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
| session | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
| configureSessionHandler | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
| registerErrorhandler | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| setDefaultTimezone | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
12 | |||
| mvc | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
| scripts | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
| getMaintenanceService | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getSessionCookieService | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildRequest | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU General Public License |
| 6 | * as published by the Free Software Foundation; under version 2 |
| 7 | * of the License (non-upgradable). |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program; if not, write to the Free Software |
| 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 17 | * |
| 18 | * Copyright (c) 2002-2008 (original work) Public Research Centre Henri Tudor & University of Luxembourg |
| 19 | * (under the project TAO & TAO2); |
| 20 | * 2009-2012 (update and modification) Public Research Centre Henri Tudor |
| 21 | * (under the project TAO-SUSTAIN & TAO-DEV); |
| 22 | * 2013-2020 (update and modification) Open Assessment Technologies SA; |
| 23 | * |
| 24 | */ |
| 25 | |
| 26 | namespace oat\tao\model\mvc; |
| 27 | |
| 28 | use common_exception_PreConditionFailure as PreConditionException; |
| 29 | use common_exception_SystemUnderMaintenance as MaintenanceException; |
| 30 | use common_ext_ExtensionsManager; |
| 31 | use common_Logger; |
| 32 | use common_report_Report as Report; |
| 33 | use common_session_SessionManager as SessionManager; |
| 34 | use Exception; |
| 35 | use GuzzleHttp\Psr7\Response; |
| 36 | use GuzzleHttp\Psr7\ServerRequest; |
| 37 | use helpers_Report as ReportHelper; |
| 38 | use oat\oatbox\event\EventAggregator; |
| 39 | use oat\oatbox\service\ServiceConfigDriver; |
| 40 | use oat\oatbox\service\ServiceManager; |
| 41 | use oat\oatbox\service\ServiceManagerAwareInterface; |
| 42 | use oat\oatbox\service\ServiceManagerAwareTrait; |
| 43 | use oat\tao\helpers\Template; |
| 44 | use oat\tao\model\asset\AssetService; |
| 45 | use oat\tao\model\http\RequestRebuilder; |
| 46 | use oat\tao\model\maintenance\Maintenance; |
| 47 | use oat\tao\model\mvc\error\ExceptionInterpreterService; |
| 48 | use oat\tao\model\routing\CliController; |
| 49 | use oat\tao\model\routing\TaoFrontController; |
| 50 | use oat\tao\model\session\Business\Contract\SessionCookieServiceInterface; |
| 51 | use Psr\Http\Message\RequestInterface; |
| 52 | use Request; |
| 53 | use tao_helpers_Context; |
| 54 | use tao_helpers_Request; |
| 55 | use tao_helpers_Scriptloader as ScriptLoader; |
| 56 | |
| 57 | /** |
| 58 | * The Bootstrap Class enables you to drive the application flow for a given extenstion. |
| 59 | * A bootstrap instance initialize the context and starts all the services: |
| 60 | * - session |
| 61 | * - database |
| 62 | * - user |
| 63 | * |
| 64 | * And it's used to disptach the Control Loop |
| 65 | * - control the platform status (redirect to the maintenance page if it is required) |
| 66 | * - dispatch to the convenient action |
| 67 | * - control code exceptions |
| 68 | * |
| 69 | * @author Bertrand CHEVRIER <bertrand.chevrier@tudor.lu> |
| 70 | * @package tao |
| 71 | * @example |
| 72 | * <code> |
| 73 | * $bootStrap = new BootStrap('tao'); //create the Bootstrap instance |
| 74 | * $bootStrap->start(); //start all the services |
| 75 | * $bootStrap->dispatch(); //dispatch the http request into the control loop |
| 76 | * </code> |
| 77 | */ |
| 78 | class Bootstrap implements ServiceManagerAwareInterface |
| 79 | { |
| 80 | use ServiceManagerAwareTrait; |
| 81 | |
| 82 | public const CONFIG_SESSION_HANDLER = 'session'; |
| 83 | |
| 84 | /** |
| 85 | * @var boolean if the context has been started |
| 86 | */ |
| 87 | protected static $isStarted = false; |
| 88 | |
| 89 | /** |
| 90 | * @var boolean if the context has been dispatched |
| 91 | */ |
| 92 | protected static $isDispatched = false; |
| 93 | |
| 94 | /** |
| 95 | * Bootstrap constructor. |
| 96 | * |
| 97 | * Initialize the context |
| 98 | * |
| 99 | * @param $configuration |
| 100 | * @throws \common_Exception If config file is not readable |
| 101 | */ |
| 102 | public function __construct($configuration) |
| 103 | { |
| 104 | new DotEnvReader(); |
| 105 | |
| 106 | if (! is_string($configuration) || ! is_readable($configuration)) { |
| 107 | throw new PreConditionException('TAO platform seems to be not installed.'); |
| 108 | } |
| 109 | |
| 110 | require_once $configuration; |
| 111 | $serviceManager = new ServiceManager( |
| 112 | (new ServiceConfigDriver())->connect('config', [ |
| 113 | 'dir' => dirname($configuration), |
| 114 | 'humanReadable' => true |
| 115 | ]) |
| 116 | ); |
| 117 | |
| 118 | $this->setServiceLocator($serviceManager); |
| 119 | // To be removed when getServiceManager will disappear |
| 120 | ServiceManager::setServiceManager($serviceManager); |
| 121 | if (PHP_SAPI === 'cli') { |
| 122 | tao_helpers_Context::load('SCRIPT_MODE'); |
| 123 | } else { |
| 124 | tao_helpers_Context::load('APP_MODE'); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * Check if the current context has been started |
| 130 | * @return boolean |
| 131 | */ |
| 132 | public static function isStarted() |
| 133 | { |
| 134 | return self::$isStarted; |
| 135 | } |
| 136 | |
| 137 | /** |
| 138 | * Check if the current context has been dispatched |
| 139 | * @return boolean |
| 140 | */ |
| 141 | public static function isDispatched() |
| 142 | { |
| 143 | return self::$isDispatched; |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Check if the platform is ready |
| 148 | * |
| 149 | * @return boolean Return true if the application is ready |
| 150 | */ |
| 151 | protected function isReady() |
| 152 | { |
| 153 | return $this->getMaintenanceService()->isPlatformReady(); |
| 154 | } |
| 155 | |
| 156 | /** |
| 157 | * Start all the services: |
| 158 | * 1. Start the session |
| 159 | * 2. Update the include path |
| 160 | * 3. Include the global helpers |
| 161 | * 4. Connect the current user to the generis API |
| 162 | * 5. Initialize the internationalization |
| 163 | * 6. Check the application' state |
| 164 | */ |
| 165 | public function start() |
| 166 | { |
| 167 | if (!self::$isStarted) { |
| 168 | $this->session(); |
| 169 | $this->setDefaultTimezone(); |
| 170 | $this->registerErrorhandler(); |
| 171 | self::$isStarted = true; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | protected function dispatchHttp() |
| 176 | { |
| 177 | $isAjax = tao_helpers_Request::isAjax(); |
| 178 | |
| 179 | if (tao_helpers_Context::check('APP_MODE')) { |
| 180 | if (!$isAjax) { |
| 181 | $this->scripts(); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | //Catch all exceptions |
| 186 | try { |
| 187 | //the app is ready, process mvc |
| 188 | if ($this->isReady()) { |
| 189 | $this->mvc(); |
| 190 | } else { //the app is not ready, put platform on maintenance |
| 191 | $this->displayMaintenancePage(); |
| 192 | } |
| 193 | } catch (Exception $e) { |
| 194 | $this->catchError($e); |
| 195 | } |
| 196 | |
| 197 | // explicitly close session |
| 198 | session_write_close(); |
| 199 | } |
| 200 | |
| 201 | /** |
| 202 | * Put the platform on maintenance |
| 203 | * Redirect to maintenance page if http call is not ajax |
| 204 | * Otherwise throw common_exception_SystemUnderMaintenance |
| 205 | * |
| 206 | * @throws MaintenanceException |
| 207 | */ |
| 208 | protected function displayMaintenancePage() |
| 209 | { |
| 210 | //the request is not an ajax request, redirect the user to the maintenance page |
| 211 | if (! tao_helpers_Request::isAjax()) { |
| 212 | require_once Template::getTemplate('error/maintenance.tpl', 'tao'); |
| 213 | //else throw an exception, this exception will be send to the client properly |
| 214 | } else { |
| 215 | throw new MaintenanceException(); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | |
| 220 | protected function dispatchCli() |
| 221 | { |
| 222 | $params = $_SERVER['argv']; |
| 223 | array_shift($params); |
| 224 | |
| 225 | if (count($params) < 1) { |
| 226 | $report = new Report(Report::TYPE_ERROR, __('No action specified')); |
| 227 | } else { |
| 228 | $actionIdentifier = array_shift($params); |
| 229 | $cliController = new CliController(); |
| 230 | $this->propagate($cliController); |
| 231 | $report = $cliController->runAction($actionIdentifier, $params); |
| 232 | } |
| 233 | |
| 234 | echo ReportHelper::renderToCommandline($report); |
| 235 | } |
| 236 | |
| 237 | /** |
| 238 | * Dispatch the current http request into the control loop: |
| 239 | * 1. Load the ressources |
| 240 | * 2. Start the MVC Loop from the ClearFW |
| 241 | * manage Exception: |
| 242 | */ |
| 243 | public function dispatch() |
| 244 | { |
| 245 | if (!self::$isDispatched) { |
| 246 | if (PHP_SAPI === 'cli') { |
| 247 | $this->dispatchCli(); |
| 248 | } else { |
| 249 | $this->dispatchHttp(); |
| 250 | } |
| 251 | |
| 252 | $this->getServiceManager()->get(EventAggregator::SERVICE_ID)->triggerAggregatedEvents(); |
| 253 | |
| 254 | self::$isDispatched = true; |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | /** |
| 259 | * Catch any errors |
| 260 | * return a http response in function of client accepted mime type |
| 261 | * |
| 262 | * @param Exception $exception |
| 263 | */ |
| 264 | protected function catchError(Exception $exception) |
| 265 | { |
| 266 | $exceptionInterpreterService = $this->getServiceLocator()->get(ExceptionInterpreterService::SERVICE_ID); |
| 267 | $interpretor = $exceptionInterpreterService->getExceptionInterpreter($exception); |
| 268 | $interpretor->getResponse()->send(); |
| 269 | } |
| 270 | |
| 271 | /** |
| 272 | * Start the session |
| 273 | */ |
| 274 | protected function session() |
| 275 | { |
| 276 | if (PHP_SAPI === 'cli') { |
| 277 | return; |
| 278 | } |
| 279 | |
| 280 | if (tao_helpers_Context::check('APP_MODE')) { |
| 281 | // Set a specific ID to the session. |
| 282 | $request = new Request(); |
| 283 | if ($request->hasParameter('session_id')) { |
| 284 | session_id($request->getParameter('session_id')); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | // set the session cookie to HTTP only. |
| 289 | |
| 290 | $this->configureSessionHandler(); |
| 291 | |
| 292 | $this->getSessionCookieService()->initializeSessionCookie(); |
| 293 | } |
| 294 | |
| 295 | private function configureSessionHandler() |
| 296 | { |
| 297 | $sessionHandler = common_ext_ExtensionsManager::singleton() |
| 298 | ->getExtensionById('tao') |
| 299 | ->getConfig(self::CONFIG_SESSION_HANDLER); |
| 300 | |
| 301 | if ($sessionHandler !== false) { |
| 302 | session_set_save_handler( |
| 303 | [$sessionHandler, 'open'], |
| 304 | [$sessionHandler, 'close'], |
| 305 | [$sessionHandler, 'read'], |
| 306 | [$sessionHandler, 'write'], |
| 307 | [$sessionHandler, 'destroy'], |
| 308 | [$sessionHandler, 'gc'] |
| 309 | ); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * register a custom Errorhandler |
| 315 | */ |
| 316 | protected function registerErrorhandler() |
| 317 | { |
| 318 | // register the logger as erorhandler |
| 319 | common_Logger::singleton()->register(); |
| 320 | } |
| 321 | |
| 322 | /** |
| 323 | * Set Timezone quickfix |
| 324 | */ |
| 325 | protected function setDefaultTimezone() |
| 326 | { |
| 327 | if (function_exists("date_default_timezone_set") && defined('TIME_ZONE')) { |
| 328 | date_default_timezone_set(TIME_ZONE); |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | /** |
| 333 | * Start the MVC Loop from the ClearFW |
| 334 | * @throws \ActionEnforcingException in case of wrong module or action |
| 335 | * @throws \tao_models_classes_UserException when a request try to acces a protected area |
| 336 | */ |
| 337 | protected function mvc() |
| 338 | { |
| 339 | $request = $this->buildRequest(); |
| 340 | $response = new Response(); |
| 341 | $frontController = $this->propagate(new TaoFrontController()); |
| 342 | $frontController($request, $response); |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * Load external resources for the current context |
| 347 | * @see \tao_helpers_Scriptloader |
| 348 | */ |
| 349 | protected function scripts() |
| 350 | { |
| 351 | $assetService = $this->getServiceLocator()->get(AssetService::SERVICE_ID); |
| 352 | $cssFiles = [ |
| 353 | $assetService->getAsset('css/layout.css', 'tao'), |
| 354 | $assetService->getAsset('css/tao-main-style.css', 'tao'), |
| 355 | $assetService->getAsset('css/tao-3.css', 'tao') |
| 356 | ]; |
| 357 | |
| 358 | //stylesheets to load |
| 359 | ScriptLoader::addCssFiles($cssFiles); |
| 360 | |
| 361 | if (SessionManager::isAnonymous()) { |
| 362 | ScriptLoader::addCssFile( |
| 363 | $assetService->getAsset('css/portal.css', 'tao') |
| 364 | ); |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | protected function getMaintenanceService(): Maintenance |
| 369 | { |
| 370 | /** @noinspection PhpIncompatibleReturnTypeInspection */ |
| 371 | return $this->getServiceLocator()->get(Maintenance::SERVICE_ID); |
| 372 | } |
| 373 | |
| 374 | private function getSessionCookieService(): SessionCookieServiceInterface |
| 375 | { |
| 376 | /** @noinspection PhpIncompatibleReturnTypeInspection */ |
| 377 | return $this->getServiceLocator()->get(SessionCookieServiceInterface::class); |
| 378 | } |
| 379 | |
| 380 | private function buildRequest(): RequestInterface |
| 381 | { |
| 382 | $request = ServerRequest::fromGlobals(); |
| 383 | return (new RequestRebuilder())->rebuild($request); |
| 384 | } |
| 385 | } |