tao-test/app/taoQtiItem/controller/QtiCreator.php

357 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) 2013-2019 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*
*
*/
namespace oat\taoQtiItem\controller;
use common_exception_BadRequest;
use common_exception_Error;
use core_kernel_classes_Resource;
use oat\generis\model\data\event\ResourceUpdated;
use oat\generis\model\OntologyAwareTrait;
use oat\oatbox\event\EventManager;
use oat\tao\model\http\HttpJsonResponseTrait;
use oat\tao\model\media\MediaService;
use oat\tao\model\TaoOntology;
use oat\taoItems\model\event\ItemCreatedEvent;
use oat\taoItems\model\media\ItemMediaResolver;
use oat\taoQtiItem\helpers\Authoring;
use oat\taoQtiItem\model\CreatorConfig;
use oat\taoQtiItem\model\event\ItemCreatorLoad;
use oat\taoQtiItem\model\HookRegistry;
use oat\taoQtiItem\model\ItemModel;
use oat\taoQtiItem\model\qti\event\UpdatedItemEventDispatcher;
use oat\taoQtiItem\model\qti\exception\QtiModelException;
use oat\taoQtiItem\model\qti\Item;
use oat\taoQtiItem\model\qti\parser\XmlToItemParser;
use oat\taoQtiItem\model\qti\Service;
use oat\taoQtiItem\model\qti\validator\ItemIdentifierValidator;
use tao_actions_CommonModule;
use tao_helpers_File;
use tao_helpers_Http;
use tao_helpers_Uri;
use taoItems_models_classes_ItemsService;
/**
* QtiCreator Controller provide actions to edit a QTI item
*
* @author CRP Henri Tudor - TAO Team - {@link http://www.tao.lu}
* @package taoQTI
* @license GPLv2 http://www.opensource.org/licenses/gpl-2.0.php
*/
class QtiCreator extends tao_actions_CommonModule
{
use OntologyAwareTrait;
use HttpJsonResponseTrait;
/**
* @return EventManager
*/
protected function getEventManager()
{
return $this->getServiceLocator()->get(EventManager::SERVICE_ID);
}
/**
* create a new QTI item
*
* @throws common_exception_BadRequest
* @throws common_exception_Error
*
* @requiresRight id WRITE
* @requiresRight classUri WRITE
*/
public function createItem()
{
if (!\tao_helpers_Request::isAjax()) {
throw new common_exception_BadRequest('wrong request mode');
}
try {
$this->validateCsrf();
} catch (\common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$clazz = new \core_kernel_classes_Resource($this->getRequestParameter('id'));
if ($clazz->isClass()) {
$clazz = new \core_kernel_classes_Class($clazz);
} else {
foreach ($clazz->getTypes() as $type) {
// determine class from selected instance
$clazz = $type;
break;
}
}
$service = \taoItems_models_classes_ItemsService::singleton();
$label = $service->createUniqueLabel($clazz);
$item = $service->createInstance($clazz, $label);
if ($item !== null) {
$service->setItemModel($item, new \core_kernel_classes_Resource(ItemModel::MODEL_URI));
$this->getEventManager()->trigger(new ItemCreatedEvent($item->getUri()));
$response = [
'success' => true,
'message' => __('Successfully created item "%s"', $item->getLabel()),
'label' => $item->getLabel(),
'uri' => $item->getUri()
];
} else {
$response = false;
}
$this->returnJson($response);
}
public function index()
{
if (!$this->hasRequestParameter('instance')) {
throw new common_exception_Error('The item creator needs to be opened with an item');
}
$item = new core_kernel_classes_Resource(tao_helpers_Uri::decode($this->getRequestParameter('instance')));
$config = $this->getCreatorConfig($item);
$this->setData('config', $config->toArray());
$this->setView('QtiCreator/index.tpl');
$this->getEventManager()->trigger(new ItemCreatorLoad());
}
public function getMediaSources()
{
$exclude = '';
if ($this->hasRequestParameter('exclude')) {
$exclude = $this->getRequestParameter('exclude');
}
// get the config media Sources
$sources = array_keys(MediaService::singleton()->getBrowsableSources());
$mediaSources = [];
if ($exclude !== 'local') {
$mediaSources[] = ['root' => 'local', 'path' => '/'];
}
foreach ($sources as $source) {
if ($source !== $exclude) {
$mediaSources[] = ['root' => $source, 'path' => 'taomedia://' . $source . '/'];
}
}
$this->returnJson($mediaSources);
}
public function getItemData()
{
$returnValue = [
'itemData' => null
];
if ($this->hasRequestParameter('uri')) {
$lang = taoItems_models_classes_ItemsService::singleton()->getSessionLg();
$itemUri = tao_helpers_Uri::decode($this->getRequestParameter('uri'));
$itemResource = new core_kernel_classes_Resource($itemUri);
$item = Service::singleton()->getDataItemByRdfItem($itemResource, $lang, false);//do not resolve xinclude here, leave it to the client side
if (!is_null($item)) {
$returnValue['itemData'] = $item->toArray();
}
$availableLangs = \tao_helpers_I18n::getAvailableLangsByUsage(new core_kernel_classes_Resource(TaoOntology::PROPERTY_STANCE_LANGUAGE_USAGE_DATA));
$returnValue['languagesList'] = $availableLangs;
}
$this->returnJson($returnValue);
}
public function saveItem()
{
$returnValue = ['success' => false];
$request = $this->getPsrRequest();
$queryParams = $request->getQueryParams();
if (isset($queryParams['uri'])) {
$xml = $request->getBody()->getContents();
$rdfItem = $this->getResource(urldecode($queryParams['uri']));
/** @var Service $itemService */
$itemService = $this->getServiceLocator()->get(Service::class);
if ($itemService->hasItemModel($rdfItem, [ItemModel::MODEL_URI])) {
try {
$this->validateXmlInput($xml);
Authoring::checkEmptyMedia($xml);
$item = $this->getXmlToItemParser()->parseAndSanitize($xml);
$this->getItemIdentifierValidator()->validate($item);
$returnValue['success'] = $itemService->saveDataItemToRdfItem($item, $rdfItem);
$this->getEventManager()->trigger(new ResourceUpdated($rdfItem));
$this->getUpdatedItemEventDispatcher()->dispatch($item, $rdfItem);
} catch (QtiModelException $e) {
$this->logError($e->getMessage());
$returnValue = [
'success' => false,
'type' => 'Error',
'message' => $e->getUserMessage()
];
} catch (common_exception_Error $e) {
$this->logError(sprintf('Item XML is not valid: %s', $e->getMessage()));
$returnValue = [
'success' => false,
'type' => 'Error',
'message' => 'Item XML is not valid'
];
}
}
}
$this->returnJson($returnValue);
}
public function getFile()
{
if (
$this->hasRequestParameter('uri')
&& $this->hasRequestParameter('lang')
&& $this->hasRequestParameter('relPath')
) {
$uri = urldecode($this->getRequestParameter('uri'));
$rdfItem = new core_kernel_classes_Resource($uri);
$lang = urldecode($this->getRequestParameter('lang'));
$rawParams = $this->getRequest()->getRawParameters();
$relPath = ltrim(rawurldecode($rawParams['relPath']), '/');
$this->renderFile($rdfItem, $relPath, $lang);
}
}
private function renderFile($item, $path, $lang)
{
if (tao_helpers_File::securityCheck($path, true)) {
$resolver = new ItemMediaResolver($item, $lang);
$asset = $resolver->resolve($path);
$mediaSource = $asset->getMediaSource();
$stream = $mediaSource->getFileStream($asset->getMediaIdentifier());
$info = $mediaSource->getFileInfo($asset->getMediaIdentifier());
tao_helpers_Http::returnStream($stream, $info['mime']);
} else {
throw new common_exception_Error('invalid item preview file path');
}
}
/**
* Get the configuration of the Item Creator
* @param core_kernel_classes_Resource $item the selected item
* @return CreatorConfig the configration
*/
protected function getCreatorConfig(core_kernel_classes_Resource $item)
{
$config = new CreatorConfig();
$ext = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem');
$creatorConfig = $ext->getConfig('qtiCreator');
if (is_array($creatorConfig)) {
foreach ($creatorConfig as $prop => $value) {
$config->setProperty($prop, $value);
}
}
$config->setProperty('uri', $item->getUri());
$config->setProperty('label', $item->getLabel());
//set the current data lang in the item content to keep the integrity
//@todo : allow preview in a language other than the one in the session
$lang = \common_session_SessionManager::getSession()->getDataLanguage();
$config->setProperty('lang', $lang);
//base url:
$url = tao_helpers_Uri::url('getFile', 'QtiCreator', 'taoQtiItem', [
'uri' => $item->getUri(),
'lang' => $lang,
'relPath' => ''
]);
$config->setProperty('baseUrl', $url);
//map the multi column config to the plugin
//TODO migrate the config
if ($config->getProperty('multi-column') == true) {
$config->addPlugin('blockAdder', 'taoQtiItem/qtiCreator/plugins/content/blockAdder', 'content');
}
if ($config->getProperty('scrollable-multi-column') === true) {
$config->addPlugin('layoutEditor', 'taoQtiItem/qtiCreator/plugins/panel/layoutEditor', 'panel');
}
$mediaSourcesUrl = tao_helpers_Uri::url(
'getMediaSources',
'QtiCreator',
'taoQtiItem'
);
$config->setProperty('mediaSourcesUrl', $mediaSourcesUrl);
//initialize all registered hooks:
$hookClasses = HookRegistry::getRegistry()->getMap();
foreach ($hookClasses as $hookClass) {
$hook = new $hookClass();
$hook->init($config);
}
$config->init();
return $config;
}
private function getUpdatedItemEventDispatcher(): UpdatedItemEventDispatcher
{
return $this->getServiceLocator()->get(UpdatedItemEventDispatcher::class);
}
private function getXmlToItemParser(): XmlToItemParser
{
return $this->getServiceLocator()->get(XmlToItemParser::class);
}
private function getItemIdentifierValidator(): ItemIdentifierValidator
{
return $this->getServiceLocator()->get(ItemIdentifierValidator::class);
}
/**
* Check if given string is a valid xml. Throw common_exception_Error if not.
*
* @param string $xml
* @throws common_exception_Error
*/
private function validateXmlInput(string $xml)
{
if (trim($xml) === '') {
throw new common_exception_Error('Empty string given');
}
\tao_helpers_Xml::getSimpleXml($xml);
}
}