tao-test/app/tao/actions/class.RdfController.php

1439 lines
47 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);
* 2013-2021 (update and modification) Open Assessment Technologies SA;
*/
use oat\oatbox\event\EventManager;
use oat\oatbox\user\User;
use oat\generis\model\OntologyAwareTrait;
use oat\generis\model\OntologyRdfs;
use oat\tao\model\event\ResourceMovedEvent;
use oat\tao\model\search\tasks\IndexTrait;
use oat\tao\model\accessControl\PermissionChecker;
use oat\tao\model\controller\SignedFormInstance;
use oat\tao\model\lock\LockManager;
use oat\tao\model\menu\ActionService;
use oat\tao\model\menu\MenuService;
use oat\tao\model\ClassProperty\RemoveClassPropertyService;
use oat\tao\model\metadata\exception\InconsistencyConfigException;
use oat\tao\model\resources\ResourceService;
use oat\tao\model\security\SecurityException;
use oat\tao\model\security\SignatureGenerator;
use oat\tao\model\security\SignatureValidator;
use tao_helpers_form_FormContainer as FormContainer;
use oat\tao\model\ClassProperty\AddClassPropertyFormFactory;
/**
* The TaoModule is an abstract controller,
* the tao children extensions Modules should extends the TaoModule to beneficiate the shared methods.
* It regroups the methods that can be applied on any extension (the rdf:Class managment for example)
*
* @author CRP Henri Tudor - TAO Team - {@link http://www.tao.lu}
* @license GPLv2 http://www.opensource.org/licenses/gpl-2.0.php
* @package tao
*/
abstract class tao_actions_RdfController extends tao_actions_CommonModule
{
use OntologyAwareTrait;
use IndexTrait;
/** @var SignatureValidator */
protected $signatureValidator;
public function __construct()
{
// maybe some day we can inject it
$this->signatureValidator = new SignatureValidator();
parent::__construct();
}
/**
* The Modules access the models throught the service instance
*
* @var tao_models_classes_Service
*/
protected $service = null;
/**
* @return tao_models_classes_ClassService
*/
protected function getClassService()
{
if (is_null($this->service)) {
throw new common_exception_Error('No service defined for ' . get_called_class());
}
return $this->service;
}
/**
* @param string $uri
*
* @throws common_exception_Error
* @throws SecurityException
*/
protected function validateInstanceRoot($uri)
{
$instance = $this->getResource($uri);
$root = $this->getRootClass();
if ($instance->isClass()) {
$class = new core_kernel_classes_Class($instance->getUri());
if (!($class->isSubClassOf($root) || $class->equals($root))) {
throw new SecurityException(
sprintf(
'Security issue: class %s is not a subclass of %s',
$instance->getLabel(),
$root->getLabel()
)
);
}
return;
}
if (!$instance->isInstanceOf($root)) {
throw new SecurityException(sprintf(
'Security issue: instance %s is not a child of %s',
$instance->getLabel(),
$root->getLabel()
));
}
}
protected function validateInstancesRoot($uris)
{
foreach ($uris as $uri) {
$this->validateInstanceRoot($uri);
}
}
/**
* If you want strictly to check if the resource is locked,
* you should use LockManager::getImplementation()->isLocked($resource)
* Controller level convenience method to check if @resource is being locked, prepare data ans sets view,
*
* @param core_kernel_classes_Resource $resource
* @param $view
*
* @return boolean
*/
protected function isLocked($resource, $view = null)
{
$lock = LockManager::getImplementation()->getLockData($resource);
if (!is_null($lock) && $lock->getOwnerId() != $this->getSession()->getUser()->getIdentifier()) {
//if (LockManager::getImplementation()->isLocked($resource)) {
$params = [
'id' => $resource->getUri(),
'topclass-label' => $this->getRootClass()->getLabel()
];
if (!is_null($view)) {
$params['view'] = $view;
$params['ext'] = Context::getInstance()->getExtensionName();
}
$this->forward('locked', 'Lock', 'tao', $params);
}
return false;
}
/**
* get the current item class regarding the classUri' request parameter
* @return core_kernel_classes_Class the item class
* @throws Exception
*/
protected function getCurrentClass()
{
$classUri = tao_helpers_Uri::decode($this->getRequestParameter('classUri'));
if (is_null($classUri) || empty($classUri)) {
$class = null;
$resource = $this->getCurrentInstance();
foreach ($resource->getTypes() as $type) {
$class = $type;
break;
}
if (is_null($class)) {
throw new Exception("No valid class uri found");
}
$returnValue = $class;
} else {
if (!common_Utils::isUri($classUri)) {
throw new tao_models_classes_MissingRequestParameterException('classUri - expected to be valid URI');
}
$returnValue = $this->getClass($classUri);
}
return $returnValue;
}
/**
* ! Please override me !
* get the current instance regarding the uri and classUri in parameter
* @param string $parameterName
*
* @return core_kernel_classes_Resource
* @throws tao_models_classes_MissingRequestParameterException
*/
protected function getCurrentInstance($parameterName = 'uri')
{
$uri = tao_helpers_Uri::decode($this->getRequestParameter($parameterName));
$this->validateUri($uri);
return $this->getResource($uri);
}
protected function validateUri($uri)
{
if (!common_Utils::isUri($uri)) {
throw new tao_models_classes_MissingRequestParameterException('uri');
}
}
/**
* get the main class
* @return core_kernel_classes_Class
*/
abstract protected function getRootClass();
public function editClassProperties()
{
return $this->forward('index', 'PropertiesAuthoring', 'tao');
}
/**
* Deprecated alias for getClassForm
*
* @deprecated
*/
protected function editClass(core_kernel_classes_Class $class, core_kernel_classes_Resource $resource, core_kernel_classes_Class $topclass = null)
{
return $this->getClassForm($class, $resource, $topclass);
}
protected function getClassForm($class, $resource, $topclass = null)
{
$controller = new tao_actions_PropertiesAuthoring();
$controller->setServiceLocator($this->getServiceLocator());
return $controller->getClassForm($class);
}
/*
* Actions
*/
/**
* Main action
* @return void
*/
public function index()
{
$this->setView('index.tpl');
}
/**
* Renders json data from the current ontology root class.
*
* The possible request parameters are the following:
*
* * uniqueNode: A URI indicating the returned hiearchy will be a single class, with a single children corresponding to the URI.
* * browse:
* * hideInstances:
* * chunk:
* * offset:
* * limit:
* * subclasses:
* * classUri:
*
* @return void
* @requiresRight classUri READ
*/
public function getOntologyData()
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_IsAjaxAction(__FUNCTION__);
}
$options = $this->getTreeOptionsFromRequest([]);
//generate the tree from the given parameters
$tree = $this->getClassService()->toTree($options['class'], $options);
//retrieve resources permissions
$user = \common_Session_SessionManager::getSession()->getUser();
$permissions = $this->getResourceService()->getResourcesPermissions($user, $tree);
//expose the tree
$this->returnJson([
'tree' => $tree,
'permissions' => $permissions
]);
}
/**
* Get options to generate tree
* @return array
* @throws Exception
*/
protected function getTreeOptionsFromRequest($options = [])
{
$options = array_merge([
'subclasses' => true,
'instances' => true,
'highlightUri' => '',
'chunk' => false,
'offset' => 0,
'limit' => 0
], $options);
if ($this->hasRequestParameter('loadNode')) {
$options['uniqueNode'] = $this->getRequestParameter('loadNode');
}
if ($this->hasRequestParameter("selected")) {
$options['browse'] = [$this->getRequestParameter("selected")];
}
if ($this->hasRequestParameter('hideInstances')) {
if ((bool) $this->getRequestParameter('hideInstances')) {
$options['instances'] = false;
}
}
if ($this->hasRequestParameter('classUri')) {
$options['class'] = $this->getCurrentClass();
$options['chunk'] = !$options['class']->equals($this->getRootClass());
} else {
$options['class'] = $this->getRootClass();
}
if ($this->hasRequestParameter('offset')) {
$options['offset'] = $this->getRequestParameter('offset');
}
if ($this->hasRequestParameter('limit')) {
$options['limit'] = $this->getRequestParameter('limit');
}
if ($this->hasRequestParameter('order')) {
$options['order'] = tao_helpers_Uri::decode($this->getRequestParameter('order'));
}
if ($this->hasRequestParameter('orderdir')) {
$options['orderdir'] = $this->getRequestParameter('orderdir');
}
return $options;
}
/**
* Add permission information to the tree structure
*
* @deprecated
*
* @param array $tree
* @return array
*/
protected function addPermissions($tree)
{
$user = $this->getSession()->getUser();
$section = MenuService::getSection(
$this->getRequestParameter('extension'),
$this->getRequestParameter('perspective'),
$this->getRequestParameter('section')
);
$actions = $section->getActions();
//then compute ACL for each node of the tree
$treeKeys = array_keys($tree);
if (isset($treeKeys[0]) && is_int($treeKeys[0])) {
foreach ($tree as $index => $treeNode) {
$tree[$index] = $this->computePermissions($actions, $user, $treeNode);
}
} else {
$tree = $this->computePermissions($actions, $user, $tree);
}
return $tree;
}
/**
* compulte permissions for a node against actions
*
* @deprecated
*
* @param array[] $actions the actions data with context, name and the resolver
* @param User $user the user
* @param array $node a tree node
* @return array the node augmented with permissions
*/
private function computePermissions($actions, $user, $node)
{
if (isset($node['attributes']['data-uri'])) {
if ($node['type'] == 'class') {
$params = ['classUri' => $node['attributes']['data-uri']];
} else {
$params = [];
foreach ($node['attributes'] as $key => $value) {
if (substr($key, 0, strlen('data-')) == 'data-') {
$params[substr($key, strlen('data-'))] = $value;
}
}
}
$params['id'] = $node['attributes']['data-uri'];
$node['permissions'] = $this->getActionService()->computePermissions($actions, $user, $params);
}
if (isset($node['children'])) {
foreach ($node['children'] as $index => $child) {
$node['children'][$index] = $this->computePermissions($actions, $user, $child);
}
}
return $node;
}
/**
* Common action to view and change the label of a class
*/
public function editClassLabel()
{
$class = $this->getCurrentClass();
$signature = $this->createFormSignature();
$classUri = $class->getUri();
$hasWriteAccess = $this->hasWriteAccess($classUri) && $this->hasWriteAccessToAction(__FUNCTION__);
$editClassLabelForm = new tao_actions_form_EditClassLabel(
$class,
$this->getRequestParameters(),
$signature,
[FormContainer::CSRF_PROTECTION_OPTION => true, FormContainer::IS_DISABLED => !$hasWriteAccess]
);
$myForm = $editClassLabelForm->getForm();
if ($myForm->isSubmited() && $myForm->isValid()) {
if ($hasWriteAccess) {
$class->setLabel($myForm->getValue(tao_helpers_Uri::encode(OntologyRdfs::RDFS_LABEL)));
$this->setData('message', __('%s Class saved', $class->getLabel()));
} else {
$this->setData('errorMessage', __('You do not have the required rights to edit this resource.'));
}
$this->setData('selectNode', tao_helpers_Uri::encode($classUri));
$this->setData('reload', true);
}
$this->setData('formTitle', __('Edit class %s', \tao_helpers_Display::htmlize($class->getLabel())));
$this->setData('myForm', $myForm->render());
$this->setView('form.tpl', 'tao');
}
/**
* Add an instance of the selected class
*
* @requiresRight id WRITE
* @requiresRight classUri WRITE
*
* @throws SecurityException
* @throws InconsistencyConfigException
* @throws common_exception_BadRequest
* @throws common_exception_Error
*/
public function addInstance()
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
$id = $this->getRequestParameter('id');
$this->validateInstanceRoot($id);
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$this->signatureValidator->checkSignature(
$this->getRequestParameter('signature'),
$id
);
$response = [];
$class = $this->getClass($id);
$label = $this->getClassService()->createUniqueLabel($class);
$instance = $this->getClassService()->createInstance($class, $label);
if ($instance instanceof core_kernel_classes_Resource) {
$response = [
'success' => true,
'label' => $instance->getLabel(),
'uri' => $instance->getUri()
];
}
$this->returnJson($response);
}
/**
* Add a subclass to the currently selected class
* @requiresRight id WRITE
* @requiresRight classUri WRITE
*
* @throws Exception
* @throws common_exception_BadRequest
*/
public function addSubClass()
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
$classId = $this->getRequestParameter('id');
$this->signatureValidator->checkSignature(
$this->getRequestParameter('signature'),
$classId
);
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$this->validateInstanceRoot($classId);
$parent = $this->getClass($classId);
$class = $this->getClassService()->createSubClass($parent);
if ($class instanceof core_kernel_classes_Class) {
$this->returnJson([
'success' => true,
'label' => $class->getLabel(),
'uri' => tao_helpers_Uri::encode($class->getUri())
]);
}
}
/**
* Add an instance of the selected class
* @throws common_exception_BadRequest
* @return void
*/
public function addInstanceForm()
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
$class = $this->getCurrentClass();
$formContainer = new tao_actions_form_CreateInstance([$class],
[
FormContainer::CSRF_PROTECTION_OPTION => true,
FormContainer::ADDITIONAL_VALIDATORS => $this->getExtraValidationRules(),
tao_actions_form_CreateInstance::EXCLUDED_PROPERTIES => $this->getExcludedProperties(),
]
);
$addInstanceForm = $formContainer->getForm();
if ($addInstanceForm->isSubmited() && $addInstanceForm->isValid()) {
$properties = $addInstanceForm->getValues();
$instance = $this->createInstance([$class], $properties);
$this->setData('message', __('%s created', $instance->getLabel()));
$this->setData('reload', true);
}
$this->setData('formTitle', __('Create instance of ') . $class->getLabel());
$this->setData('myForm', $addInstanceForm->render());
$this->setView('form.tpl', 'tao');
}
/**
* creates the instance
*
* @param array $classes
* @param array $properties
* @return core_kernel_classes_Resource
*/
protected function createInstance($classes, $properties)
{
$first = array_shift($classes);
$instance = $first->createInstanceWithProperties($properties);
foreach ($classes as $class) {
$instance = $this->getResource('');
$instance->setType($class);
}
return $instance;
}
public function editInstance()
{
$class = $this->getCurrentClass();
$instance = $this->getCurrentInstance();
$myFormContainer = new SignedFormInstance(
$class,
$instance,
[
FormContainer::CSRF_PROTECTION_OPTION => true,
FormContainer::ADDITIONAL_VALIDATORS => $this->getExtraValidationRules(),
tao_actions_form_Instance::EXCLUDED_PROPERTIES => $this->getExcludedProperties()
]
);
$myForm = $myFormContainer->getForm();
if ($myForm->isSubmited() && $myForm->isValid()) {
$values = $myForm->getValues();
// save properties
$binder = new tao_models_classes_dataBinding_GenerisFormDataBinder($instance);
$binder->bind($values);
$message = __('Instance saved');
$this->setData('message', $message);
$this->setData('reload', true);
}
$this->setData('formTitle', __('Edit Instance'));
$this->setData('myForm', $myForm->render());
$this->setView('form.tpl', 'tao');
}
/**
* Duplicate the current instance
* render a JSON response
*
* @throws \League\Flysystem\FileExistsException
* @throws common_Exception
* @throws common_exception_BadRequest
* @throws common_exception_Error
* @throws tao_models_classes_MissingRequestParameterException
*
* @return void
* @requiresRight uri READ
* @requiresRight classUri WRITE
*/
public function cloneInstance()
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
$uri = $this->getRequestParameter('uri');
$this->signatureValidator->checkSignature(
$this->getRequestParameter('signature'),
$uri
);
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$this->validateInstanceRoot($uri);
$clone = $this->getClassService()->cloneInstance($this->getCurrentInstance(), $this->getCurrentClass());
if ($clone !== null) {
$this->returnJson([
'success' => true,
'message' => __('Successfully cloned instance as %s', $clone->getLabel()),
'label' => $clone->getLabel(),
'uri' => tao_helpers_Uri::encode($clone->getUri())
]);
}
}
/**
* Copy a resource to a destination
*
* @requiresRight uri READ
*/
public function copyInstance()
{
if (
$this->hasRequestParameter('destinationClassUri')
&& $this->hasRequestParameter('uri')
&& common_Utils::isUri($this->getRequestParameter('destinationClassUri'))
) {
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$this->validateInstanceRoot(
$this->getRequestParameter('uri')
);
$this->signatureValidator->checkSignature(
$this->getRequestParameter('signature'),
$this->getRequestParameter('uri')
);
$instance = $this->getCurrentInstance();
$destinationClass = $this->getClass($this->getRequestParameter('destinationClassUri'));
if ($this->hasWriteAccess($destinationClass->getUri())) {
$copy = $this->getClassService()->cloneInstance($instance, $destinationClass);
if (!is_null($copy)) {
return $this->returnJson([
'success' => true,
'data' => [
'label' => $copy->getLabel(),
'uri' => $copy->getUri()
]
]);
}
return $this->returnJson([
'success' => false,
'errorCode' => 204,
'errorMessage' => __("Unable to copy the resource")
], 204);
}
return $this->returnJson([
'success' => false,
'errorCode' => 401,
'errorMessage' => __("Permission denied to write in the selected class")
], 401);
}
return $this->returnJson([
'success' => false,
'errorCode' => 412,
'errorMessage' => __('Missing Parameters')
], 412);
}
/**
* Move an instance from a class to another
* @return void
* @requiresRight uri WRITE
* @requiresRight destinationClassUri WRITE
*/
public function moveInstance()
{
$response = [];
if ($this->hasRequestParameter('destinationClassUri') && $this->hasRequestParameter('uri')) {
$id = $this->getRequestParameter('uri');
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$this->validateInstanceRoot($id);
$this->signatureValidator->checkSignature($this->getRequestParameter('signature'), $id);
$instance = $this->getResource($id);
$types = $instance->getTypes();
$class = reset($types);
$destinationUri = tao_helpers_Uri::decode($this->getRequestParameter('destinationClassUri'));
$this->validateDestinationClass($destinationUri, $class->getUri());
$destinationClass = $this->getClass($destinationUri);
$confirmed = $this->getRequestParameter('confirmed');
if (empty($confirmed) || $confirmed == 'false' || $confirmed === false) {
$diff = $this->getClassService()->getPropertyDiff($class, $destinationClass);
if (count($diff) > 0) {
return $this->returnJson([
'status' => 'diff',
'data' => $diff
]);
}
}
$status = $this->getClassService()->changeClass($instance, $destinationClass);
$response = ['status' => $status];
}
$this->returnJson($response);
}
/**
* Move a single resource to another class
*
* @requiresRight uri WRITE
*
* @throws common_exception_Error
* @throws common_exception_MethodNotAllowed
*/
public function moveResource()
{
try {
if (!$this->hasRequestParameter('uri')) {
throw new InvalidArgumentException('Resource uri must be specified.');
}
$data = $this->getRequestParameter('uri');
$id = $data['id'];
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$this->validateUri($id);
$this->validateInstanceRoot($id);
$this->signatureValidator->checkSignature($data['signature'], $id);
$ids = [$id];
$this->validateMoveRequest();
$response = $this->moveAllInstances($ids);
$this->returnJson($response);
} catch (\InvalidArgumentException $e) {
$this->returnJsonError($e->getMessage());
}
}
/**
* Move all specififed resources to the given destination root class
*
* @throws common_exception_Error
* @throws common_exception_MethodNotAllowed
* @requiresRight id WRITE
*/
public function moveAll()
{
try {
if (!$this->hasRequestParameter('ids')) {
throw new InvalidArgumentException('Resource ids must be specified.');
}
$ids = [];
foreach ($this->getRequestParameter('ids') as $id) {
$ids[] = $id['id'];
}
$this->validateInstancesRoot($ids);
if (empty($ids)) {
throw new InvalidArgumentException('No instances specified.');
}
$this->signatureValidator->checkSignatures(
$this->getRequestParameter('ids')
);
$this->validateMoveRequest();
$response = $this->moveAllInstances($ids);
$this->returnJson($response);
} catch (\InvalidArgumentException $e) {
$this->returnJsonError($e->getMessage());
}
}
/**
* Render the form to translate a Resource instance
* @return void
* @throws common_exception_Error
* @throws tao_models_classes_MissingRequestParameterException
* @requiresRight id WRITE
*/
public function translateInstance()
{
$instance = $this->getCurrentInstance();
$formContainer = new tao_actions_form_Translate(
$this->getCurrentClass(),
$instance,
[FormContainer::CSRF_PROTECTION_OPTION => true]
);
$myForm = $formContainer->getForm();
if ($this->hasRequestParameter('target_lang')) {
$targetLang = $this->getRequestParameter('target_lang');
$availableLanguages = tao_helpers_I18n::getAvailableLangsByUsage(
$this->getResource(tao_models_classes_LanguageService::INSTANCE_LANGUAGE_USAGE_DATA)
);
if (in_array($targetLang, $availableLanguages)) {
$langElt = $myForm->getElement('translate_lang');
$langElt->setValue($targetLang);
$langElt->setAttribute('readonly', 'true');
$trData = $this->getClassService()->getTranslatedProperties($instance, $targetLang);
foreach ($trData as $key => $value) {
$element = $myForm->getElement(tao_helpers_Uri::encode($key));
if ($element !== null) {
$element->setValue($value);
}
}
}
}
if ($myForm->isSubmited() && $myForm->isValid()) {
$values = $myForm->getValues();
if (isset($values['translate_lang'])) {
$datalang = $this->getSession()->getDataLanguage();
$lang = $values['translate_lang'];
$translated = 0;
foreach ($values as $key => $value) {
if (0 === strpos($key, 'http')) {
$value = trim($value);
$property = $this->getProperty($key);
if (empty($value)) {
if ($datalang !== $lang && $lang !== '') {
$instance->removePropertyValueByLg($property, $lang);
}
} elseif ($instance->editPropertyValueByLg($property, $value, $lang)) {
$translated++;
}
}
}
if ($translated > 0) {
$this->setData('message', __('Translation saved'));
}
}
}
$this->setData('myForm', $myForm->render());
$this->setData('formTitle', __('Translate'));
$this->setView('form.tpl', 'tao');
}
/**
* load the translated data of an instance regarding the given lang
*
* @throws common_exception_BadRequest
* @throws common_exception_Error
* @throws tao_models_classes_MissingRequestParameterException
*
* @return void
*/
public function getTranslatedData()
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
$data = [];
if ($this->hasRequestParameter('lang')) {
$data = tao_helpers_Uri::encodeArray(
$this->getClassService()->getTranslatedProperties(
$this->getCurrentInstance(),
$this->getRequestParameter('lang')
),
tao_helpers_Uri::ENCODE_ARRAY_KEYS
);
}
$this->returnJson($data);
}
/**
* delete an instance or a class
* called via ajax
*
* @throws common_exception_BadRequest
* @throws common_exception_MissingParameter
*
* @requiresRight id WRITE
*/
public function delete()
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
if ($this->hasRequestParameter('uri')) {
return $this->forward('deleteResource', null, null, (['id' => tao_helpers_Uri::decode($this->getRequestParameter('uri'))]));
} elseif ($this->hasRequestParameter('classUri')) {
return $this->forward('deleteClass', null, null, (['id' => tao_helpers_Uri::decode($this->getRequestParameter('classUri'))]));
} else {
throw new common_exception_MissingParameter();
}
}
/**
* Generic resource deletion action
*
* @throws Exception
* @throws common_exception_BadRequest
* @requiresRight id WRITE
*/
public function deleteResource()
{
if (!$this->isXmlHttpRequest() || !$this->hasRequestParameter('id')) {
throw new common_exception_BadRequest('wrong request mode');
}
$id = $this->getRequestParameter('id');
// Csrf token validation
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$this->validateInstanceRoot($id);
$this->signatureValidator->checkSignature(
$this->getRequestParameter('signature'),
$id
);
$resource = $this->getResource($this->getRequestParameter('id'));
$deleted = $this->getClassService()->deleteResource($resource);
return $this->returnJson([
'success' => $deleted,
'message' => __('Successfully deleted %s', $resource->getLabel()),
'deleted' => $deleted
]);
}
/**
* Generic class deletion action
*
* @throws Exception
* @throws common_exception_BadRequest
* @requiresRight id WRITE
*/
public function deleteClass()
{
if (!$this->isXmlHttpRequest() || !$this->hasRequestParameter('id')) {
throw new common_exception_BadRequest('wrong request mode');
}
$id = $this->getRequestParameter('id');
// Csrf token validation
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$this->validateInstanceRoot($id);
$this->signatureValidator->checkSignature(
$this->getRequestParameter('signature'),
$id
);
$class = $this->getClass($id);
if ($this->getRootClass()->equals($class)) {
$success = false;
$msg = __('You cannot delete the root node');
} else {
$label = $class->getLabel();
$success = $this->getClassService()->deleteClass($class);
$msg = $success ? __('%s has been deleted', $label) : __('Unable to delete %s', $label);
}
$this->returnJson([
'success' => $success,
'message' => $msg,
'deleted' => $success
]);
}
/**
* Delete all given resources
*
* @requiresRight ids WRITE
*
* @throws Exception
* @throws common_exception_BadRequest
*/
public function deleteAll()
{
$response = [
'success' => true,
'deleted' => []
];
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
// Csrf token validation
try {
$this->validateCsrf();
} catch (common_exception_Unauthorized $e) {
$this->response = $this->getPsrResponse()->withStatus(403, __('Unable to process your request'));
return;
}
$ids = [];
foreach ($this->getRequestParameter('ids') as $id) {
$ids[] = $id['id'];
}
$this->signatureValidator->checkSignatures($this->getRequestParameter('ids'));
$this->validateInstancesRoot($ids);
foreach ($ids as $id) {
$deleted = false;
$deletedResourceLabel = '';
try {
if ($this->hasWriteAccess($id)) {
$resource = new \core_kernel_classes_Resource($id);
if ($resource->isClass()) {
$class = new \core_kernel_classes_Class($id);
$deletedResourceLabel = $class->getLabel();
$deleted = $this->getClassService()->deleteClass($class);
} else {
$deletedResourceLabel = $resource->getLabel();
$deleted = $this->getClassService()->deleteResource($resource);
}
}
} catch (\common_Exception $ce) {
\common_Logger::w('Unable to remove resource ' . $id . ' : ' . $ce->getMessage());
}
if ($deleted) {
$response['deleted'][] = $id;
}
$response['message'] = __('Successfully deleted %s', $deletedResourceLabel);
}
return $this->returnJson($response);
}
/**
* Copy of \tao_actions_PropertiesAuthoring::addClassProperty to split access between extensions
*
* @requiresRight id WRITE
*/
public function addClassProperty(AddClassPropertyFormFactory $addClassPropertyFormFactory): void
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
$myForm = $addClassPropertyFormFactory->add(
$this->getPsrRequest(),
$this->hasWriteAccessToAction(__FUNCTION__)
);
$this->setData('data', $myForm->renderElements());
$this->setView('blank.tpl', 'tao');
}
/**
* Copy of \tao_actions_PropertiesAuthoring::removeClassProperty to split access between extensions
*
* @requiresRight classUri WRITE
* @throws common_Exception
*/
public function removeClassProperty(RemoveClassPropertyService $removeClassPropertyService): void
{
if (!$this->isXmlHttpRequest()) {
throw new common_exception_BadRequest('wrong request mode');
}
$success = $removeClassPropertyService->remove($this->getPsrRequest());
if ($success) {
$this->returnJson(['success' => true]);
} else {
$this->returnError(__('Unable to remove the property.'));
}
}
/**
* Test whenever the current user has "WRITE" access to the specified id
*
* @param string $resourceId
* @return boolean
*/
protected function hasWriteAccess($resourceId)
{
/** @var PermissionChecker $permissionChecker */
$permissionChecker = $this->getServiceLocator()->get(PermissionChecker::class);
return $permissionChecker->hasWriteAccess($resourceId);
}
/**
* Validate request with all required parameters
*
* @throws common_exception_Error
* @throws common_exception_MethodNotAllowed If it is not POST method
* @throws InvalidArgumentException If parameters are not correct
*/
protected function validateMoveRequest()
{
if (!$this->isRequestPost()) {
throw new common_exception_MethodNotAllowed('Only POST method is allowed to move instances.');
}
if (!$this->hasRequestParameter('destinationClassUri')) {
throw new InvalidArgumentException('Destination class must be specified');
}
$destinationClass = new \core_kernel_classes_Class($this->getRequestParameter('destinationClassUri'));
if (!$destinationClass->isClass()) {
throw new InvalidArgumentException('Destination class must be a valid class');
}
}
/**
* Move instances to another class
*
* {
* "destinationClassUri": "http://test.it",
* "ids": [
* "http://resource1",
* "http://resource2",
* "http://class1",
* "http://class2"
* ]
* }
* @requiresRight destinationClassUri WRITE
* @params array $ids The list of instance uris to move
*
* @throws common_exception_Error
*/
protected function moveAllInstances(array $ids)
{
$rootClass = $this->getClassService()->getRootClass();
if (in_array($rootClass->getUri(), $ids)) {
throw new InvalidArgumentException(sprintf('Root class "%s" cannot be moved', $rootClass->getUri()));
}
$destinationClass = new \core_kernel_classes_Class($this->getRequestParameter('destinationClassUri'));
if (!$destinationClass->isSubClassOf($rootClass) && $destinationClass->getUri() != $rootClass->getUri()) {
throw new InvalidArgumentException(sprintf('Instance "%s" cannot be moved to another root class', $destinationClass->getUri()));
}
[$statuses, $instances, $classes] = $this->getInstancesList($ids);
$movableInstances = $this->getInstancesToMove($classes, $instances, $statuses);
$statuses = $this->move($destinationClass, $movableInstances, $statuses);
return [
'success' => true,
'data' => $statuses
];
}
/**
* Gets list of existing instances/classes
*
* @param array $ids list of ids asked to be moved
* @return array
*/
private function getInstancesList(array $ids)
{
$statuses = $instances = $classes = [];
foreach ($ids as $key => $instance) {
$instance = $this->getResource($instance);
if ($instance->isClass()) {
$instance = $this->getClass($instance);
$classes[] = $instance;
} elseif (!$instance->exists()) {
$statuses[$instance->getUri()] = [
'success' => false,
'message' => sprintf('Instance "%s" does not exist', $instance->getUri()),
];
break;
}
$instances[$key] = $instance;
}
return [
$statuses,
$instances,
$classes
];
}
/**
* Get movable instances from the list of instances
*
* @param array $classes
* @param array $instances
*
* @return array
*/
private function getInstancesToMove(array $classes = [], array $instances = [], array &$statuses = [])
{
$movableInstances = [];
// Check if a class belong to class to move
/** @var core_kernel_classes_Resource|core_kernel_classes_Class $instance */
foreach ($instances as $instance) {
$isValid = true;
foreach ($classes as $class) {
if ($instance instanceof core_kernel_classes_Class) {
//Disallow moving a class to $class. True only for classes which are already subclasses of $class
if ($class->getUri() != $instance->getUri() && $instance->isSubClassOf($class)) {
$statuses[$instance->getUri()] = [
'success' => false,
'message' => sprintf('Instance "%s" cannot be moved to class to move "%s"', $instance->getUri(), $class->getUri()),
];
$isValid = false;
break;
}
} else {
//Disallow moving instances to $class. True only for instances which already belongs to $class
if ($instance->isInstanceOf($class)) {
$statuses[$instance->getUri()] = [
'success' => false,
'message' => sprintf('Instance "%s" cannot be moved to class to move "%s"', $instance->getUri(), $class->getUri()),
];
$isValid = false;
break;
}
}
}
if ($isValid) {
$movableInstances[$instance->getUri()] = $instance;
}
}
return $movableInstances;
}
/**
* Move movableInstances to the destinationClass
*
* @param core_kernel_classes_Class $destinationClass class to move to
* @param array $movableInstances list of instances available to move
* @param array $statuses list of statuses for instances asked to be moved
*
* @return array $statuses updated list of statuses
*/
private function move(\core_kernel_classes_Class $destinationClass, array $movableInstances = [], array $statuses = [])
{
/** @var core_kernel_classes_Resource $movableInstance */
foreach ($movableInstances as $movableInstance) {
$statuses[$movableInstance->getUri()] = [
'success' => $success = $this->getClassService()->changeClass($movableInstance, $destinationClass),
];
if ($success === true) {
$statuses[$movableInstance->getUri()]['message'] = sprintf(
'Instance "%s" has been successfully moved to "%s"',
$movableInstance->getUri(),
$destinationClass->getUri()
);
$this->getEventManager()->trigger(new ResourceMovedEvent($movableInstance, $destinationClass));
} else {
$statuses[$movableInstance->getUri()]['message'] = sprintf('An error has occurred while persisting instance "%s"', $movableInstance->getUri());
}
}
return $statuses;
}
/**
* Return a formatted error message with code 406
*
* @param $message
* @param int $httpStatusCode
*/
protected function returnJsonError($message, $httpStatusCode = 406)
{
$response = [
'success' => false,
'errorCode' => $httpStatusCode,
'errorMessage' => $message
];
$this->returnJson($response, 406);
}
protected function getExtraValidationRules(): array
{
return [];
}
protected function getExcludedProperties(): array
{
return [];
}
/**
* @return ActionService
*/
private function getActionService()
{
return $this->getServiceLocator()->get(ActionService::SERVICE_ID);
}
/**
* Get the resource service
* @return ResourceService
*/
protected function getResourceService()
{
return $this->getServiceLocator()->get(ResourceService::SERVICE_ID);
}
/**
* @return SignatureGenerator
*/
private function getSignatureGenerator()
{
return $this->getServiceLocator()->get(SignatureGenerator::SERVICE_ID);
}
/**
* @return string
*
* @throws InconsistencyConfigException
*/
private function createFormSignature()
{
return $this->getSignatureGenerator()->generate(
tao_helpers_Uri::encode($this->getRequestParameter('classUri'))
);
}
/**
* @param $destinationUri
* @param $currentClassUri
*/
private function validateDestinationClass($destinationUri, $currentClassUri)
{
$destinationClass = $this->getClass($destinationUri);
if (empty($destinationUri) || $destinationUri === $currentClassUri || !$destinationClass->exists()) {
throw new InvalidArgumentException('Wrong destination class uri');
}
}
private function getEventManager(): EventManager
{
return $this->getServiceLocator()->get(EventManager::SERVICE_ID);
}
}