tao-test/app/taoQtiTest/models/classes/TestCategoryPresetProvider.php

284 lines
8.7 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2017 (original work) Open Assessment Technologies SA;
*/
declare(strict_types=1);
/**
* @author Christophe Noël <christophe@taotesting.com>
*/
namespace oat\taoQtiTest\models;
use oat\oatbox\service\ConfigurableService;
use oat\taoTests\models\runner\plugins\TestPluginService;
use RuntimeException;
class TestCategoryPresetProvider extends ConfigurableService
{
public const SERVICE_ID = 'taoQtiTest/CategoryPresetProvider';
public const GROUP_NAVIGATION = 'navigation';
public const GROUP_WARNING = 'warning';
public const GROUP_TOOLS = 'tools';
private $allPresets;
private $isGroomed = false;
/**
* TestCategoryPresetProvider constructor.
*
* @param array $options
* @param array $allPresets - allow override of preset list
*/
public function __construct(array $options = [], array $allPresets = [])
{
$this->allPresets = $allPresets;
parent::__construct($options);
}
/**
* @param string $presetGroup
* @param TestCategoryPreset[]|TestCategoryPreset $presets
*/
public function register(string $presetGroup, $presets): void
{
if (!array_key_exists($presetGroup, $this->allPresets)) {
return;
}
if (!is_array($presets)) {
$presets = [$presets];
}
foreach ($presets as $preset) {
/** @noinspection TypeUnsafeArraySearchInspection */
if (!in_array($preset, $this->allPresets[$presetGroup]['presets'])) {
$this->allPresets[$presetGroup]['presets'][] = $preset;
}
}
}
/**
* Get all active presets
*
* @param bool $keepGroupKeys if `true` returns groups mapped to their group IDs
*
* @return array - the sorted preset list
*/
public function getPresets(bool $keepGroupKeys = false): array
{
if (empty($this->allPresets)) {
$this->loadPresetFromProviders();
}
$this->groomPresets();
return $keepGroupKeys ? $this->allPresets : array_values($this->allPresets);
}
public function findPresetGroupOrFail(string $groupId): array
{
$presets = $this->getPresets(true);
if (!isset($presets[$groupId])) {
throw new RuntimeException("Failed to fetch #$groupId preset group.");
}
return $presets[$groupId];
}
/**
* Get all active presets matching the given config.
*
* If a preset is linked to a feature flag,
* we add it only if the config value matching the flag is true.
*
* For example, if a $aPreset->featureFlag = 'foo';
* The preset will be included only if $config['foo'] = true.
*
* If the config doesn't have a flag, we keep the preset.
*
* @param array $config a config flag list as { key : string => value : boolean }
*
* @return array the sorted preset list
*/
public function getAvailablePresets(array $config = []): array
{
//work on a clone
$presets = array_merge([], $this->getPresets());
foreach ($presets as $groupId => &$presetGroup) {
if (isset($presetGroup['presets'])) {
//filter presets based on the config value
//if the config has the flag, we check it's value
//if the config doesn't have the flag, we keep the preset
$presetGroup['presets'] = array_filter(
$presetGroup['presets'],
function ($preset) use ($config) {
return $this->isPresetAvailable($preset, $config);
}
);
//remove empty groups
if (count($presetGroup['presets']) === 0) {
unset($presets[$groupId]);
}
}
}
return $presets;
}
protected function getPresetGroups(): array
{
return [
self::GROUP_NAVIGATION => [
'groupId' => self::GROUP_NAVIGATION,
'groupLabel' => __('Test Navigation'),
'groupOrder' => 100,
'presets' => [],
],
self::GROUP_WARNING => [
'groupId' => self::GROUP_WARNING,
'groupLabel' => __('Navigation Warnings'),
'groupOrder' => 200,
'presets' => [],
],
self::GROUP_TOOLS => [
'groupId' => self::GROUP_TOOLS,
'groupLabel' => __('Test-Taker Tools'),
'groupOrder' => 300,
'presets' => [],
],
];
}
/**
* Is a preset available according to a configuration (ie. based on it's featureFlag)
*
* @param TestCategoryPreset $preset the preset to test
* @param array $config the configuration
*
* @return boolean true if available
*/
private function isPresetAvailable(TestCategoryPreset $preset, array $config = []): bool
{
$flag = $preset->getFeatureFlag();
return !$flag || !isset($config[$flag]) || $config[$flag];
}
private function loadPresetFromProviders(): void
{
$this->allPresets = $this->getPresetGroups();
$providersRegistry = TestCategoryPresetRegistry::getRegistry();
$allProviders = $providersRegistry->getMap();
if (!empty($allProviders)) {
foreach ($allProviders as $providerClass) {
if (class_exists($providerClass)) {
$providerInstance = new $providerClass();
$providerInstance->registerPresets($this);
}
}
}
}
private function filterInactivePresets(): void
{
$serviceLocator = $this->getServiceLocator();
$pluginService = $serviceLocator->get(TestPluginService::SERVICE_ID);
$allEmptyGroups = [];
if (!empty($this->allPresets)) {
foreach ($this->allPresets as $groupId => &$presetGroup) {
if (!empty($presetGroup['presets'])) {
$presetGroup['presets'] = array_filter(
$presetGroup['presets'],
static function (TestCategoryPreset $preset) use ($pluginService): bool {
$presetPluginId = $preset->getPluginId();
if (!empty($presetPluginId)) {
$presetPlugin = $pluginService->getPlugin($presetPluginId);
return ($presetPlugin !== null) ? $presetPlugin->isActive() : false;
}
return true;
}
);
}
if (empty($presetGroup['presets'])) {
$allEmptyGroups[] = $groupId;
}
}
unset($presetGroup);
}
// finally, remove empty groups, if any
if (!empty($allEmptyGroups)) {
foreach ($allEmptyGroups as $emptyGroupId) {
unset($this->allPresets[$emptyGroupId]);
}
}
}
private function sortPresets(): void
{
// sort presets groups
uasort(
$this->allPresets,
static function (array $a, array $b): int {
return $a['groupOrder'] <=> $b['groupOrder'];
}
);
// sort presets
foreach ($this->allPresets as &$presetGroup) {
if (!empty($presetGroup)) {
usort(
$presetGroup['presets'],
static function (TestCategoryPreset $a, TestCategoryPreset $b): int {
return $a->getOrder() <=> $b->getOrder();
}
);
}
}
}
private function groomPresets(): void
{
if ($this->isGroomed) {
return;
}
$this->filterInactivePresets();
$this->sortPresets();
$this->isGroomed = true;
}
}