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); } }