*/ namespace oat\taoQtiTest\models\runner\config; use oat\oatbox\service\ConfigurableService; use oat\taoQtiTest\models\runner\config\Business\Contract\OverriddenOptionsRepositoryInterface; use oat\taoQtiTest\models\runner\RunnerServiceContext; use oat\taoQtiTest\models\SectionPauseService; /** * Class QtiRunnerOptions * @package oat\taoQtiTest\models\runner\options */ class QtiRunnerConfig extends ConfigurableService implements RunnerConfig { public const SERVICE_ID = 'taoQtiTest/QtiRunnerConfig'; public const OPTION_CONFIG = 'config'; public const CATEGORY_OPTION_PREFIX = 'x-tao-option-'; /** * @deprecated since version 29.5.0, to be removed in 30.0.0. Use QtiRunnerService::TOOL_ITEM_THEME_SWITCHER instead */ const TOOL_ITEM_THEME_SWITCHER = 'itemThemeSwitcher'; /** * @deprecated since version 29.5.0, to be removed in 30.0.0. Use QtiRunnerService::TOOL_ITEM_THEME_SWITCHER_KEY instead */ const TOOL_ITEM_THEME_SWITCHER_KEY = 'taoQtiTest/runner/plugins/tools/itemThemeSwitcher/itemThemeSwitcher'; const TARGET_CLIENT = 'client'; /** * The test runner config * @var array */ protected $config; /** * The test runner currently activated options * @var array */ protected $options; /** * Returns the config of the test runner * @return array|mixed * @throws \common_ext_ExtensionException */ protected function buildConfig() { if ($this->hasOption(self::OPTION_CONFIG)) { // load the configuration from service $config = $this->getOption(self::OPTION_CONFIG); } else { // fallback to get the raw server config, using the old notation $rawConfig = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiTest')->getConfig('testRunner'); // build the test config using the new notation $target = isset($rawConfig['timer']) && isset($rawConfig['timer']['target']) ? $rawConfig['timer']['target'] : null; $config = [ 'timerWarning' => isset($rawConfig['timerWarning']) ? $rawConfig['timerWarning'] : null, 'timerWarningForScreenreader' => isset($rawConfig['timerWarningForScreenreader']) ? $rawConfig['timerWarningForScreenreader'] : null, 'catEngineWarning' => isset($rawConfig['catEngineWarning']) ? $rawConfig['catEngineWarning'] : null, 'progressIndicator' => [ 'type' => isset($rawConfig['progress-indicator']) ? $rawConfig['progress-indicator'] : null, 'renderer' => isset($rawConfig['progress-indicator-renderer']) ? $rawConfig['progress-indicator-renderer'] : null, 'scope' => isset($rawConfig['progress-indicator-scope']) ? $rawConfig['progress-indicator-scope'] : null, 'forced' => isset($rawConfig['progress-indicator-forced']) ? $rawConfig['progress-indicator-forced'] : false, 'showLabel' => !empty($rawConfig['progress-indicator-show-label']), 'showTotal' => !empty($rawConfig['progress-indicator-show-total']), 'categories' => isset($rawConfig['progress-categories']) ? $rawConfig['progress-categories'] : [], ], 'review' => [ 'enabled' => !empty($rawConfig['test-taker-review']), 'scope' => isset($rawConfig['test-taker-review-scope']) ? $rawConfig['test-taker-review-scope'] : null, 'useTitle' => !empty($rawConfig['test-taker-review-use-title']), 'forceTitle' => !empty($rawConfig['test-taker-review-force-title']), 'forceInformationalTitle' => !empty($rawConfig['test-taker-review-force-informational-title']), 'showLegend' => !empty($rawConfig['test-taker-review-show-legend']), 'defaultOpen' => !empty($rawConfig['test-taker-review-default-open']), 'itemTitle' => isset($rawConfig['test-taker-review-item-title']) ? $rawConfig['test-taker-review-item-title'] : null, 'informationalItemTitle' => isset($rawConfig['test-taker-review-informational-item-title']) ? $rawConfig['test-taker-review-informational-item-title'] : null, 'preventsUnseen' => !empty($rawConfig['test-taker-review-prevents-unseen']), 'canCollapse' => !empty($rawConfig['test-taker-review-can-collapse']), 'displaySubsectionTitle' => !empty($rawConfig['test-taker-review-display-subsection-title']), 'allowSkipahead' => isset($rawConfig['test-taker-review-skipahead']) ? $rawConfig['test-taker-review-skipahead'] : false, ], 'exitButton' => !empty($rawConfig['exitButton']), 'nextSection' => !empty($rawConfig['next-section']), 'plugins' => isset($rawConfig['plugins']) ? $rawConfig['plugins'] : null, 'security' => [ 'csrfToken' => isset($rawConfig['csrf-token']) ? $rawConfig['csrf-token'] : false, ], 'timer' => [ 'target' => $target, 'resetAfterResume' => !empty($rawConfig['reset-timer-after-resume']), 'keepUpToTimeout' => !empty($rawConfig['keep-timer-up-to-timeout']), 'restoreTimerFromClient' => $target === self::TARGET_CLIENT, ], 'enableAllowSkipping' => isset($rawConfig['enable-allow-skipping']) ? $rawConfig['enable-allow-skipping'] : false, 'enableValidateResponses' => isset($rawConfig['enable-validate-responses']) ? $rawConfig['enable-validate-responses'] : false, 'checkInformational' => isset($rawConfig['check-informational']) ? $rawConfig['check-informational'] : false, 'enableUnansweredItemsWarning' => isset($rawConfig['test-taker-unanswered-items-message']) ? $rawConfig['test-taker-unanswered-items-message'] : true, 'allowShortcuts' => !empty($rawConfig['allow-shortcuts']), 'shortcuts' => isset($rawConfig['shortcuts']) ? $rawConfig['shortcuts'] : [], 'itemCaching' => [ 'enabled' => isset($rawConfig['allow-browse-next-item']) ? $rawConfig['allow-browse-next-item'] : false, 'amount' => isset($rawConfig['item-cache-size']) ? intval($rawConfig['item-cache-size']) : 3, ], 'guidedNavigation' => isset($rawConfig['guidedNavigation']) ? $rawConfig['guidedNavigation'] : false, 'toolStateServerStorage' => isset($rawConfig['tool-state-server-storage']) ? $rawConfig['tool-state-server-storage'] : [], 'forceEnableLinearNextItemWarning' => isset($rawConfig['force-enable-linear-next-item-warning']) ? $rawConfig['force-enable-linear-next-item-warning'] : false, 'enableLinearNextItemWarningCheckbox' => isset($rawConfig['enable-linear-next-item-warning-checkbox']) ? $rawConfig['enable-linear-next-item-warning-checkbox'] : true, ]; } return $config; } /** * Returns the config of the test runner * @return mixed */ public function getConfig() { if (is_null($this->config)) { // build the test config using the new notation $this->config = $this->buildConfig(); } return $this->config; } /** * Returns the value of a config entry. * The name can be a namespace, each name being separated by a dot, like: 'itemCaching.enabled' * @param string $name * @return mixed */ public function getConfigValue($name) { $config = $this->getConfig(); $path = explode('.', (string)$name); foreach ($path as $entry) { if (isset($config[$entry])) { $config =& $config[$entry]; } else { return null; } } return $config; } /** * Returns the options related to the current test context * @param RunnerServiceContext $context The test context * @return mixed */ protected function buildOptions(RunnerServiceContext $context) { $session = $context->getTestSession(); // Comment allowed? Skipping allowed? Logout or Exit allowed ? $options = [ 'allowComment' => \taoQtiTest_helpers_TestRunnerUtils::doesAllowComment($session), 'allowSkipping' => \taoQtiTest_helpers_TestRunnerUtils::doesAllowSkipping($session), 'exitButton' => \taoQtiTest_helpers_TestRunnerUtils::doesAllowExit($session, $context), 'logoutButton' => \taoQtiTest_helpers_TestRunnerUtils::doesAllowLogout($session), 'validateResponses' => \taoQtiTest_helpers_TestRunnerUtils::doesValidateResponses($session), 'sectionPause' => $this->getSectionPauseService()->couldBePaused($session) ]; // get the options from the categories owned by the current item $categories = $this->getCategories($context); $prefixCategoryLen = strlen(self::CATEGORY_OPTION_PREFIX); foreach ($categories as $category) { if (!strncmp($category, self::CATEGORY_OPTION_PREFIX, $prefixCategoryLen)) { // extract the option name from the category, transform to camelCase if needed $optionName = lcfirst(str_replace(' ', '', ucwords(strtr(substr($category, $prefixCategoryLen), ['-' => ' ', '_' => ' '])))); // the options added by the categories are just flags $options[$optionName] = true; } } foreach ($this->getOverriddenOptionsRepository()->findAll() as $option) { $options[$option->getId()] = $option->isEnabled(); } return $options; } /** * Returns the options related to the current test context * @param RunnerServiceContext $context The test context * @return mixed */ public function getTestOptions(RunnerServiceContext $context) { if (is_null($this->options)) { // build the test config using the new notation $this->options = $this->buildOptions($context); } return $this->options; } /** * Get Categories. * * Get the categories of the current AssessmentItemRef in the route depending on a given $context. * * @param RunnerServiceContext $context * @return array An array of strings. */ protected function getCategories(RunnerServiceContext $context) { return $context->getCurrentAssessmentItemRef()->getCategories()->getArrayCopy(); } private function getSectionPauseService(): SectionPauseService { /** @noinspection PhpIncompatibleReturnTypeInspection */ return $this->getServiceLocator()->get(SectionPauseService::SERVICE_ID); } private function getOverriddenOptionsRepository(): OverriddenOptionsRepositoryInterface { /** @noinspection PhpIncompatibleReturnTypeInspection */ return $this->getServiceLocator()->get(OverriddenOptionsRepositoryInterface::SERVICE_ID); } }