344 lines
12 KiB
PHP
344 lines
12 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) 2002-2008 (original work) Public Research Centre Henri Tudor & University of Luxembourg (under the project TAO & TAO2);
|
||
|
* 2008-2010 (update and modification) Deutsche Institut für Internationale Pädagogische Forschung (under the project TAO-TRANSFER);
|
||
|
* 2009-2012 (update and modification) Public Research Centre Henri Tudor (under the project TAO-SUSTAIN & TAO-DEV);
|
||
|
* 2012-2021 (original work) Open Assessment Technologies SA;
|
||
|
*/
|
||
|
use oat\generis\model\OntologyRdf;
|
||
|
use oat\tao\model\TaoOntology;
|
||
|
use oat\taoTests\models\event\TestCreatedEvent;
|
||
|
use oat\taoTests\models\event\TestDuplicatedEvent;
|
||
|
use oat\taoTests\models\event\TestRemovedEvent;
|
||
|
use oat\generis\model\fileReference\FileReferenceSerializer;
|
||
|
use oat\tao\model\service\ServiceFileStorage;
|
||
|
use oat\taoTests\models\TestModel;
|
||
|
use oat\taoTests\models\MissingTestmodelException;
|
||
|
use oat\tao\model\OntologyClassService;
|
||
|
|
||
|
/**
|
||
|
* Service methods to manage the Tests business models using the RDF API.
|
||
|
*
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
*/
|
||
|
class taoTests_models_classes_TestsService extends OntologyClassService
|
||
|
{
|
||
|
|
||
|
public const CLASS_TEST_MODEL = 'http://www.tao.lu/Ontologies/TAOTest.rdf#TestModel';
|
||
|
|
||
|
public const PROPERTY_TEST_MODEL_IMPLEMENTATION = 'http://www.tao.lu/Ontologies/TAOTest.rdf#TestModelImplementation';
|
||
|
|
||
|
public const PROPERTY_TEST_TESTMODEL = 'http://www.tao.lu/Ontologies/TAOTest.rdf#TestTestModel';
|
||
|
|
||
|
/** @deprecated self::PROPERTY_TEST_CONTENT should be used */
|
||
|
public const TEST_TESTCONTENT_PROP = 'http://www.tao.lu/Ontologies/TAOTest.rdf#TestContent';
|
||
|
|
||
|
public const PROPERTY_TEST_CONTENT = 'http://www.tao.lu/Ontologies/TAOTest.rdf#TestContent';
|
||
|
|
||
|
/**
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
*/
|
||
|
public function deleteTest(core_kernel_classes_Resource $test): bool
|
||
|
{
|
||
|
$returnValue = false;
|
||
|
if (!is_null($test)) {
|
||
|
try {
|
||
|
//delete the associated content
|
||
|
$model = $this->getTestModel($test);
|
||
|
$impl = $this->getTestModelImplementation($model);
|
||
|
$impl->deleteContent($test);
|
||
|
} catch (MissingTestmodelException $e) {
|
||
|
// no content present, skip
|
||
|
}
|
||
|
$returnValue = $test->delete();
|
||
|
$this->getEventManager()->trigger(new TestRemovedEvent($test->getUri()));
|
||
|
}
|
||
|
return (bool) $returnValue;
|
||
|
}
|
||
|
|
||
|
public function deleteResource(core_kernel_classes_Resource $resource): bool
|
||
|
{
|
||
|
return $this->deleteTest($resource);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @author Joel Bout, <joel@taotesting.com>
|
||
|
*/
|
||
|
public function getRootClass(): core_kernel_classes_Class
|
||
|
{
|
||
|
return $this->getClass(TaoOntology::CLASS_URI_TEST);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the Class in parameter is a subclass of Test
|
||
|
*
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
*/
|
||
|
public function isTestClass(core_kernel_classes_Class $clazz): bool
|
||
|
{
|
||
|
if ($clazz->getUri() == $this->getClass(TaoOntology::CLASS_URI_TEST)->getUri()) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
foreach ($this->getClass(TaoOntology::CLASS_URI_TEST)->getSubClasses(true) as $subclass) {
|
||
|
if ($clazz->getUri() == $subclass->getUri()) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
* @deprecated use $this->deleteClass instead
|
||
|
*/
|
||
|
public function deleteTestClass(core_kernel_classes_Class $clazz): bool
|
||
|
{
|
||
|
return $this->deleteClass($clazz);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
*/
|
||
|
public function getAllItems(): array
|
||
|
{
|
||
|
$returnValue = [];
|
||
|
|
||
|
$itemClazz = $this->getClass(TaoOntology::CLASS_URI_ITEM);
|
||
|
foreach ($itemClazz->getInstances(true) as $instance) {
|
||
|
$returnValue[$instance->getUri()] = $instance->getLabel();
|
||
|
}
|
||
|
|
||
|
return $returnValue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used to be called whenever the label of the Test changed
|
||
|
* Deprecated in favor of eventmanager
|
||
|
*
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
* @deprecated
|
||
|
*/
|
||
|
public function onChangeTestLabel(core_kernel_classes_Resource $test = null)
|
||
|
{
|
||
|
common_Logger::w('Call to deprecated ' . __FUNCTION__);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
*/
|
||
|
public function cloneInstance(core_kernel_classes_Resource $instance, core_kernel_classes_Class $clazz = null): ?core_kernel_classes_Resource
|
||
|
{
|
||
|
$returnValue = null;
|
||
|
|
||
|
//call the parent create instance to prevent useless process test to be created:
|
||
|
$label = $instance->getLabel();
|
||
|
$cloneLabel = "$label bis";
|
||
|
$clone = parent::createInstance($clazz, $cloneLabel);
|
||
|
|
||
|
if (!is_null($clone)) {
|
||
|
$noCloningProperties = [
|
||
|
self::PROPERTY_TEST_CONTENT,
|
||
|
OntologyRdf::RDF_TYPE
|
||
|
];
|
||
|
|
||
|
foreach ($clazz->getProperties(true) as $property) {
|
||
|
if (!in_array($property->getUri(), $noCloningProperties)) {
|
||
|
//allow clone of every property value but the deliverycontent, which is a process:
|
||
|
foreach ($instance->getPropertyValues($property) as $propertyValue) {
|
||
|
$clone->setPropertyValue($property, $propertyValue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//Fix label
|
||
|
if (preg_match("/bis/", $label)) {
|
||
|
$cloneNumber = (int)preg_replace("/^(.?)*bis/", "", $label);
|
||
|
$cloneNumber++;
|
||
|
$cloneLabel = preg_replace("/bis(.?)*$/", "", $label) . "bis $cloneNumber" ;
|
||
|
}
|
||
|
$clone->setLabel($cloneLabel);
|
||
|
|
||
|
$impl = $this->getTestModelImplementation($this->getTestModel($instance));
|
||
|
$impl->cloneContent($instance, $clone);
|
||
|
|
||
|
$this->getEventManager()->trigger(new TestDuplicatedEvent($instance->getUri(), $clone->getUri()));
|
||
|
|
||
|
$returnValue = $clone;
|
||
|
}
|
||
|
|
||
|
return $returnValue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @author Lionel Lecaque, lionel@taotesting.com
|
||
|
*/
|
||
|
protected function setDefaultModel(core_kernel_classes_Resource $test)
|
||
|
{
|
||
|
$testModelClass = $this->getClass(self::CLASS_TEST_MODEL);
|
||
|
$models = $testModelClass->getInstances();
|
||
|
if (count($models) > 0) {
|
||
|
$this->setTestModel($test, current($models));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
*/
|
||
|
public function createInstance(core_kernel_classes_Class $clazz, $label = ''): core_kernel_classes_Resource
|
||
|
{
|
||
|
$test = parent::createInstance($clazz, $label);
|
||
|
$this->setDefaultModel($test);
|
||
|
|
||
|
$this->getEventManager()->trigger(new TestCreatedEvent($test->getUri()));
|
||
|
|
||
|
return $test;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
||
|
*/
|
||
|
public function getTestItems(core_kernel_classes_Resource $test): array
|
||
|
{
|
||
|
try {
|
||
|
$model = $this->getTestModel($test);
|
||
|
$returnValue = $this->getTestModelImplementation($model)->getItems($test);
|
||
|
} catch (MissingTestmodelException $e) {
|
||
|
$returnValue = [];
|
||
|
}
|
||
|
return $returnValue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Changes the model of the test, while trying
|
||
|
* to carry over the items of the test
|
||
|
*/
|
||
|
public function setTestModel(core_kernel_classes_Resource $test, core_kernel_classes_Resource $testModel): void
|
||
|
{
|
||
|
$current = $test->getOnePropertyValue($this->getProperty(self::PROPERTY_TEST_TESTMODEL));
|
||
|
// did the model change?
|
||
|
if (is_null($current) || !$current->equals($testModel)) {
|
||
|
$items = [];
|
||
|
if (!is_null($current)) {
|
||
|
$former = $this->getTestModelImplementation($current);
|
||
|
if (!empty($former)) {
|
||
|
$items = $former->getItems($test);
|
||
|
$former->deleteContent($test);
|
||
|
}
|
||
|
}
|
||
|
$test->editPropertyValues($this->getProperty(self::PROPERTY_TEST_TESTMODEL), $testModel);
|
||
|
$newImpl = $this->getTestModelImplementation($testModel);
|
||
|
if (!empty($newImpl)) {
|
||
|
$newImpl->prepareContent($test, $items);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a compiler instance for a given test
|
||
|
*/
|
||
|
public function getCompiler(core_kernel_classes_Resource $test, ServiceFileStorage $storage): tao_models_classes_Compiler
|
||
|
{
|
||
|
$testModel = $this->getTestModelImplementation($this->getTestModel($test));
|
||
|
if ($testModel instanceof TestModel) {
|
||
|
$compiler = $testModel->getCompiler($test, $storage);
|
||
|
} else {
|
||
|
$testCompilerClass = $testModel->getCompilerClass();
|
||
|
$compiler = new $testCompilerClass($test, $storage);
|
||
|
$compiler->setServiceLocator($storage->getServiceLocator());
|
||
|
}
|
||
|
return $compiler;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the class of the compiler
|
||
|
* @deprecated $this->getCompiler should be used
|
||
|
*/
|
||
|
public function getCompilerClass(core_kernel_classes_Resource $test): string
|
||
|
{
|
||
|
$testModel = $this->getTestModel($test);
|
||
|
return $this->getTestModelImplementation($testModel)->getCompilerClass();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the model of the current test
|
||
|
*
|
||
|
* @throws MissingTestmodelException
|
||
|
*/
|
||
|
public function getTestModel(core_kernel_classes_Resource $test): core_kernel_classes_Resource
|
||
|
{
|
||
|
$testModel = $test->getOnePropertyValue($this->getPropertyByUri(self::PROPERTY_TEST_TESTMODEL));
|
||
|
|
||
|
if (is_null($testModel)) {
|
||
|
throw new MissingTestmodelException('Undefined testmodel for test ' . $test->getUri());
|
||
|
}
|
||
|
|
||
|
return $testModel;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the implementation of an items test model
|
||
|
*/
|
||
|
public function getTestModelImplementation(core_kernel_classes_Resource $testModel): taoTests_models_classes_TestModel
|
||
|
{
|
||
|
$serviceId = (string)$testModel->getOnePropertyValue($this->getPropertyByUri(self::PROPERTY_TEST_MODEL_IMPLEMENTATION));
|
||
|
|
||
|
if (empty($serviceId)) {
|
||
|
throw new common_exception_NoImplementation('No implementation found for testmodel ' . $testModel->getUri());
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$testModelService = $this->getServiceManager()->get($serviceId);
|
||
|
} catch (\oat\oatbox\service\ServiceNotFoundException $e) {
|
||
|
if (!class_exists($serviceId)) {
|
||
|
throw new common_exception_Error('Test model service ' . $serviceId . ' not found');
|
||
|
}
|
||
|
// for backward compatibility support classname instead of a serviceid
|
||
|
common_Logger::w('Outdated model definition "' . $serviceId . '", please use test model service');
|
||
|
$testModelService = new $serviceId();
|
||
|
}
|
||
|
|
||
|
if (!$testModelService instanceof \taoTests_models_classes_TestModel) {
|
||
|
throw new common_exception_Error('Test model service ' . get_class($testModelService) . ' not compatible for test model ' . $testModel->getUri());
|
||
|
}
|
||
|
|
||
|
return $testModelService;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @deprecated $this->getProperty should be used
|
||
|
*/
|
||
|
public function getPropertyByUri(string $uri): core_kernel_classes_Property
|
||
|
{
|
||
|
return $this->getProperty($uri);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get serializer to persist filesystem object
|
||
|
*/
|
||
|
protected function getFileReferenceSerializer(): FileReferenceSerializer
|
||
|
{
|
||
|
return $this->getServiceManager()->get(FileReferenceSerializer::SERVICE_ID);
|
||
|
}
|
||
|
|
||
|
public function hasItems(core_kernel_classes_Resource $test): bool
|
||
|
{
|
||
|
return !empty($this->getTestItems($test));
|
||
|
}
|
||
|
}
|