tao-test/app/tao/models/classes/GenerisTreeFactory.php

327 lines
11 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);
* 2017 (update and modification) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*
*
* Factory to prepare the ontology data for the
* javascript generis tree
*
* @access public
* @author Joel Bout, <joel@taotesting.com>
* @package tao
*/
namespace oat\tao\model;
use core_kernel_classes_Class;
use core_kernel_classes_Resource;
use oat\generis\model\kernel\persistence\smoothsql\search\filter\Filter;
use oat\generis\model\OntologyRdfs;
use oat\oatbox\service\ServiceManager;
use oat\tao\helpers\TreeHelper;
use oat\tao\model\security\SignatureGenerator;
use tao_helpers_Uri;
use oat\generis\model\kernel\persistence\smoothsql\search\ComplexSearchService;
use oat\search\helper\SupportedOperatorHelper;
use oat\generis\model\OntologyAwareTrait;
class GenerisTreeFactory
{
use OntologyAwareTrait;
/**
* All instances of those classes loaded, independent of current limit ( Contain uris only )
* @var array
*/
private $browsableTypes = [];
/**
* @var int
*/
private $limit;
/**
* @var int
*/
private $offset;
/**
* @var array
*/
private $openNodes = [];
/**
* @var bool
*/
private $showResources;
/**
* @var array contains filters to apply to searchInstances
*/
private $propertyFilter = [];
/**
* @var array
*/
private $optionsFilter = [];
/** @var array */
private $extraProperties = [];
/**
* @param boolean $showResources If `true` resources will be represented in thee. Otherwise only classes.
* @param array $openNodes Class uris for which children array should be build as well
* @param int $limit Limit of resources to be shown in one class
* @param int $offset Offset for resources in one class
* @param array $resourceUrisToShow All siblings of this resources will be loaded, independent of current limit
* @param array $propertyFilter Additional property filters to apply to the tree
* @param array $optionsFilter
* @param array $extraProperties
*/
public function __construct($showResources, array $openNodes = [], $limit = 10, $offset = 0, array $resourceUrisToShow = [], array $propertyFilter = [], array $optionsFilter = [], array $extraProperties = [])
{
$this->limit = (int) $limit;
$this->offset = (int) $offset;
$this->openNodes = $openNodes;
$this->showResources = $showResources;
$this->propertyFilter = $propertyFilter;
$this->optionsFilter = $optionsFilter;
$this->extraProperties = $extraProperties;
$types = [];
foreach ($resourceUrisToShow as $uri) {
$resource = new core_kernel_classes_Resource($uri);
$types[] = $resource->getTypes();
}
if ($types) {
$this->browsableTypes = array_keys(call_user_func_array('array_merge', $types));
}
}
/**
* builds the data for a generis tree
* @param core_kernel_classes_Class $class
* @return array
*/
public function buildTree(core_kernel_classes_Class $class)
{
return $this->classToNode($class, null);
}
/**
* Builds a class node including it's content
*
* @param core_kernel_classes_Class $class
* @param core_kernel_classes_Class $parent
* @return array
* @throws
*/
private function classToNode(core_kernel_classes_Class $class, core_kernel_classes_Class $parent = null)
{
$returnValue = $this->buildClassNode($class, $parent);
// allow the class to be opened if it contains either instances or subclasses
$subclasses = $this->getSubClasses($class);
if ($this->showResources) {
$options = array_merge(['recursive' => false], $this->optionsFilter);
$queryBuilder = $this->getQueryBuilder($class, $this->propertyFilter, $options);
$search = $this->getSearchService();
$search->setLanguage($queryBuilder, \common_session_SessionManager::getSession()->getDataLanguage());
$instancesCount = $search->getGateway()->count($queryBuilder);
if ($instancesCount > 0 || count($subclasses) > 0) {
if (in_array($class->getUri(), $this->openNodes)) {
$returnValue['state'] = 'open';
$returnValue['children'] = $this->buildChildNodes($class, $subclasses);
} else {
$returnValue['state'] = 'closed';
}
// only show the resources count if we allow resources to be viewed
$returnValue['count'] = $instancesCount;
}
} else {
if (count($subclasses) > 0) {
if (in_array($class->getUri(), $this->openNodes)) {
$returnValue['state'] = 'open';
$returnValue['children'] = $this->buildChildNodes($class, $subclasses);
} else {
$returnValue['state'] = 'closed';
}
$returnValue['count'] = 0;
}
}
return $returnValue;
}
/**
* Builds the content of a class node including it's content
*
* @param core_kernel_classes_Class $class
* @param core_kernel_classes_Class[] $subclasses
* @return array
* @throws
*/
private function buildChildNodes(core_kernel_classes_Class $class, $subclasses)
{
$children = [];
// subclasses
foreach ($subclasses as $subclass) {
$children[] = $this->classToNode($subclass, $class);
}
// resources
if ($this->showResources) {
$limit = $this->limit;
if (in_array($class->getUri(), $this->browsableTypes)) {
$limit = 0;
}
$options = array_merge([
'limit' => $limit,
'offset' => $this->offset,
'recursive' => false,
'order' => [OntologyRdfs::RDFS_LABEL => 'asc'],
], $this->optionsFilter);
$queryBuilder = $this->getQueryBuilder($class, $this->propertyFilter, $options);
$search = $this->getSearchService();
$search->setLanguage($queryBuilder, \common_session_SessionManager::getSession()->getDataLanguage());
$searchResult = $search->getGateway()->search($queryBuilder);
foreach ($searchResult as $instance) {
$children[] = TreeHelper::buildResourceNode($instance, $class, $this->extraProperties);
}
}
return $children;
}
/**
* generis tree representation of a class node
* without it's content
*
* @param core_kernel_classes_Class $class
* @param core_kernel_classes_Class $parent
*
* @return array
*/
private function buildClassNode(core_kernel_classes_Class $class, core_kernel_classes_Class $parent = null)
{
$label = $class->getLabel();
$label = empty($label) ? __('no label') : $label;
return [
'data' => _dh($label),
'type' => 'class',
'attributes' => [
'id' => tao_helpers_Uri::encode($class->getUri()),
'class' => 'node-class',
'data-uri' => $class->getUri(),
'data-classUri' => is_null($parent) ? null : $parent->getUri(),
'data-signature' => $this->getSignatureGenerator()->generate($class->getUri()),
]
];
}
/**
* @return SignatureGenerator
*/
private function getSignatureGenerator()
{
return ServiceManager::getServiceManager()->get(SignatureGenerator::class);
}
/**
* @param $class
* @param $propertyFilter
* @param $options
* @return \oat\search\QueryBuilder
* @throws
*/
private function getQueryBuilder($class, $propertyFilter, $options)
{
$search = $this->getSearchService();
$queryBuilder = $search->query();
$search->setLanguage($queryBuilder, \common_session_SessionManager::getSession()->getDataLanguage());
$query = $search->searchType($queryBuilder, $class->getUri(), $options['recursive']);
foreach ($propertyFilter as $filterProp => $filterVal) {
if ($filterVal instanceof Filter) {
$query->addCriterion($filterVal->getKey(), $filterVal->getOperator(), $filterVal->getValue());
continue;
}
if (!is_array($filterVal)) {
$filterVal = [];
}
foreach ($filterVal as $values) {
if (is_array($values)) {
$query->addCriterion($filterProp, SupportedOperatorHelper::IN, $values);
} elseif (is_string($values)) {
$query->addCriterion($filterProp, SupportedOperatorHelper::CONTAIN, $values);
}
}
}
$queryBuilder->setCriteria($query);
if (isset($options['limit'])) {
$queryBuilder->setLimit($options['limit']);
}
if (isset($options['offset'])) {
$queryBuilder->setOffset($options['offset']);
}
if (isset($options['order'])) {
$queryBuilder->sort($options['order']);
}
return $queryBuilder;
}
/**
* @return ComplexSearchService
*/
private function getSearchService()
{
return $this->getModel()->getSearchInterface();
}
/**
* @param core_kernel_classes_Class $class
* @return core_kernel_classes_Class[]
* @throws
*/
private function getSubClasses(core_kernel_classes_Class $class)
{
$search = $this->getSearchService();
$queryBuilder = $search->query();
$query = $queryBuilder->newQuery()->add(OntologyRdfs::RDFS_SUBCLASSOF)->equals($class->getUri());
$queryBuilder->setCriteria($query);
//classes always sorted by label
$order = [RDFS_LABEL => 'asc'];
$queryBuilder->sort($order);
$result = [];
$search->setLanguage($queryBuilder, \common_session_SessionManager::getSession()->getDataLanguage());
foreach ($search->getGateway()->search($queryBuilder) as $subclass) {
$result[] = $this->getClass($subclass->getUri());
}
return $result;
}
}