Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 133 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 1 |
GenerisTreeFactory | |
0.00% |
0 / 133 |
|
0.00% |
0 / 11 |
1482 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
12 | |||
buildTree | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
classToNode | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
56 | |||
buildChildNodes | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
30 | |||
buildClassNode | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
getSignatureGenerator | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getQueryBuilder | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
132 | |||
getSearchService | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSubClasses | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
mustFilterTranslations | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
12 | |||
getFeatureFlagChecker | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | /* |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; under version 2 |
7 | * of the License (non-upgradable). |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | * |
18 | * Copyright (c) 2002-2008 (original work) Public Research Centre Henri Tudor & University of Luxembourg |
19 | * (under the project TAO & TAO2); |
20 | * 2008-2010 (update and modification) Deutsche Institut für Internationale Pädagogische Forschung |
21 | * (under the project TAO-TRANSFER); |
22 | * 2009-2012 (update and modification) Public Research Centre Henri Tudor |
23 | * (under the project TAO-SUSTAIN & TAO-DEV); |
24 | * 2017 (update and modification) Open Assessment Technologies SA (under the project TAO-PRODUCT); |
25 | * |
26 | * |
27 | * Factory to prepare the ontology data for the |
28 | * javascript generis tree |
29 | * |
30 | * @access public |
31 | * @author Joel Bout, <joel@taotesting.com> |
32 | * @package tao |
33 | */ |
34 | |
35 | namespace oat\tao\model; |
36 | |
37 | use core_kernel_classes_Class; |
38 | use core_kernel_classes_Resource; |
39 | use oat\generis\model\kernel\persistence\smoothsql\search\filter\Filter; |
40 | use oat\generis\model\OntologyRdfs; |
41 | use oat\oatbox\service\ServiceManager; |
42 | use oat\tao\helpers\TreeHelper; |
43 | use oat\tao\model\featureFlag\FeatureFlagChecker; |
44 | use oat\tao\model\featureFlag\FeatureFlagCheckerInterface; |
45 | use oat\tao\model\security\SignatureGenerator; |
46 | use tao_helpers_Uri; |
47 | use oat\generis\model\kernel\persistence\smoothsql\search\ComplexSearchService; |
48 | use oat\search\helper\SupportedOperatorHelper; |
49 | use oat\generis\model\OntologyAwareTrait; |
50 | |
51 | class GenerisTreeFactory |
52 | { |
53 | use OntologyAwareTrait; |
54 | |
55 | /** |
56 | * All instances of those classes loaded, independent of current limit ( Contain uris only ) |
57 | * @var array |
58 | */ |
59 | private $browsableTypes = []; |
60 | |
61 | /** |
62 | * @var int |
63 | */ |
64 | private $limit; |
65 | |
66 | /** |
67 | * @var int |
68 | */ |
69 | private $offset; |
70 | |
71 | /** |
72 | * @var array |
73 | */ |
74 | private $openNodes = []; |
75 | |
76 | /** |
77 | * @var bool |
78 | */ |
79 | private $showResources; |
80 | |
81 | /** |
82 | * @var array contains filters to apply to searchInstances |
83 | */ |
84 | private $propertyFilter = []; |
85 | |
86 | /** |
87 | * @var array |
88 | */ |
89 | private $optionsFilter = []; |
90 | |
91 | /** @var array */ |
92 | private $extraProperties = []; |
93 | /** |
94 | * @param boolean $showResources If `true` resources will be represented in thee. Otherwise only classes. |
95 | * @param array $openNodes Class uris for which children array should be build as well |
96 | * @param int $limit Limit of resources to be shown in one class |
97 | * @param int $offset Offset for resources in one class |
98 | * @param array $resourceUrisToShow All siblings of this resources will be loaded, independent of current limit |
99 | * @param array $propertyFilter Additional property filters to apply to the tree |
100 | * @param array $optionsFilter |
101 | * @param array $extraProperties |
102 | */ |
103 | public function __construct( |
104 | $showResources, |
105 | array $openNodes = [], |
106 | $limit = 10, |
107 | $offset = 0, |
108 | array $resourceUrisToShow = [], |
109 | array $propertyFilter = [], |
110 | array $optionsFilter = [], |
111 | array $extraProperties = [] |
112 | ) { |
113 | $this->limit = (int) $limit; |
114 | $this->offset = (int) $offset; |
115 | $this->openNodes = $openNodes; |
116 | $this->showResources = $showResources; |
117 | $this->propertyFilter = $propertyFilter; |
118 | $this->optionsFilter = $optionsFilter; |
119 | $this->extraProperties = $extraProperties; |
120 | |
121 | $types = []; |
122 | foreach ($resourceUrisToShow as $uri) { |
123 | $resource = new core_kernel_classes_Resource($uri); |
124 | $types[] = $resource->getTypes(); |
125 | } |
126 | |
127 | if ($types) { |
128 | $this->browsableTypes = array_keys(call_user_func_array('array_merge', $types)); |
129 | } |
130 | } |
131 | |
132 | /** |
133 | * builds the data for a generis tree |
134 | * @param core_kernel_classes_Class $class |
135 | * @return array |
136 | */ |
137 | public function buildTree(core_kernel_classes_Class $class) |
138 | { |
139 | return $this->classToNode($class, null); |
140 | } |
141 | |
142 | /** |
143 | * Builds a class node including it's content |
144 | * |
145 | * @param core_kernel_classes_Class $class |
146 | * @param core_kernel_classes_Class $parent |
147 | * @return array |
148 | * @throws |
149 | */ |
150 | private function classToNode(core_kernel_classes_Class $class, core_kernel_classes_Class $parent = null) |
151 | { |
152 | $returnValue = $this->buildClassNode($class, $parent); |
153 | |
154 | // allow the class to be opened if it contains either instances or subclasses |
155 | $subclasses = $this->getSubClasses($class); |
156 | |
157 | if ($this->showResources) { |
158 | $options = array_merge(['recursive' => false], $this->optionsFilter); |
159 | $queryBuilder = $this->getQueryBuilder($class, $this->propertyFilter, $options); |
160 | $search = $this->getSearchService(); |
161 | $search->setLanguage($queryBuilder, \common_session_SessionManager::getSession()->getDataLanguage()); |
162 | $instancesCount = $search->getGateway()->count($queryBuilder); |
163 | |
164 | if ($instancesCount > 0 || count($subclasses) > 0) { |
165 | if (in_array($class->getUri(), $this->openNodes)) { |
166 | $returnValue['state'] = 'open'; |
167 | $returnValue['children'] = $this->buildChildNodes($class, $subclasses); |
168 | } else { |
169 | $returnValue['state'] = 'closed'; |
170 | } |
171 | |
172 | // only show the resources count if we allow resources to be viewed |
173 | $returnValue['count'] = $instancesCount; |
174 | } |
175 | } else { |
176 | if (count($subclasses) > 0) { |
177 | if (in_array($class->getUri(), $this->openNodes)) { |
178 | $returnValue['state'] = 'open'; |
179 | $returnValue['children'] = $this->buildChildNodes($class, $subclasses); |
180 | } else { |
181 | $returnValue['state'] = 'closed'; |
182 | } |
183 | |
184 | $returnValue['count'] = 0; |
185 | } |
186 | } |
187 | return $returnValue; |
188 | } |
189 | |
190 | /** |
191 | * Builds the content of a class node including it's content |
192 | * |
193 | * @param core_kernel_classes_Class $class |
194 | * @param core_kernel_classes_Class[] $subclasses |
195 | * @return array |
196 | * @throws |
197 | */ |
198 | private function buildChildNodes(core_kernel_classes_Class $class, $subclasses) |
199 | { |
200 | $children = []; |
201 | |
202 | // subclasses |
203 | foreach ($subclasses as $subclass) { |
204 | $children[] = $this->classToNode($subclass, $class); |
205 | } |
206 | // resources |
207 | if ($this->showResources) { |
208 | $limit = $this->limit; |
209 | |
210 | if (in_array($class->getUri(), $this->browsableTypes)) { |
211 | $limit = 0; |
212 | } |
213 | |
214 | $options = array_merge([ |
215 | 'limit' => $limit, |
216 | 'offset' => $this->offset, |
217 | 'recursive' => false, |
218 | 'order' => [OntologyRdfs::RDFS_LABEL => 'asc'], |
219 | ], $this->optionsFilter); |
220 | |
221 | $queryBuilder = $this->getQueryBuilder($class, $this->propertyFilter, $options); |
222 | $search = $this->getSearchService(); |
223 | $search->setLanguage($queryBuilder, \common_session_SessionManager::getSession()->getDataLanguage()); |
224 | $searchResult = $search->getGateway()->search($queryBuilder); |
225 | foreach ($searchResult as $instance) { |
226 | $children[] = TreeHelper::buildResourceNode($instance, $class, $this->extraProperties); |
227 | } |
228 | } |
229 | return $children; |
230 | } |
231 | |
232 | /** |
233 | * generis tree representation of a class node |
234 | * without it's content |
235 | * |
236 | * @param core_kernel_classes_Class $class |
237 | * @param core_kernel_classes_Class $parent |
238 | * |
239 | * @return array |
240 | */ |
241 | private function buildClassNode(core_kernel_classes_Class $class, core_kernel_classes_Class $parent = null) |
242 | { |
243 | $label = $class->getLabel(); |
244 | $label = empty($label) ? __('no label') : $label; |
245 | |
246 | return [ |
247 | 'data' => _dh($label), |
248 | 'type' => 'class', |
249 | 'attributes' => [ |
250 | 'id' => tao_helpers_Uri::encode($class->getUri()), |
251 | 'class' => 'node-class', |
252 | 'data-uri' => $class->getUri(), |
253 | 'data-classUri' => is_null($parent) ? null : $parent->getUri(), |
254 | 'data-signature' => $this->getSignatureGenerator()->generate($class->getUri()), |
255 | ] |
256 | ]; |
257 | } |
258 | |
259 | /** |
260 | * @return SignatureGenerator |
261 | */ |
262 | private function getSignatureGenerator() |
263 | { |
264 | return ServiceManager::getServiceManager()->get(SignatureGenerator::class); |
265 | } |
266 | |
267 | /** |
268 | * @param $class |
269 | * @param $propertyFilter |
270 | * @param $options |
271 | * @return \oat\search\QueryBuilder |
272 | * @throws |
273 | */ |
274 | private function getQueryBuilder($class, $propertyFilter, $options) |
275 | { |
276 | $search = $this->getSearchService(); |
277 | $queryBuilder = $search->query(); |
278 | $search->setLanguage($queryBuilder, \common_session_SessionManager::getSession()->getDataLanguage()); |
279 | $query = $search->searchType($queryBuilder, $class->getUri(), $options['recursive']); |
280 | |
281 | if ($this->mustFilterTranslations($class)) { |
282 | $query->addCriterion( |
283 | TaoOntology::PROPERTY_TRANSLATION_TYPE, |
284 | SupportedOperatorHelper::IN, |
285 | [ |
286 | TaoOntology::PROPERTY_VALUE_TRANSLATION_TYPE_ORIGINAL |
287 | ] |
288 | ); |
289 | } |
290 | |
291 | foreach ($propertyFilter as $filterProp => $filterVal) { |
292 | if ($filterVal instanceof Filter) { |
293 | $query->addCriterion($filterVal->getKey(), $filterVal->getOperator(), $filterVal->getValue()); |
294 | continue; |
295 | } |
296 | if (!is_array($filterVal)) { |
297 | $filterVal = []; |
298 | } |
299 | foreach ($filterVal as $values) { |
300 | if (is_array($values)) { |
301 | $query->addCriterion($filterProp, SupportedOperatorHelper::IN, $values); |
302 | } elseif (is_string($values)) { |
303 | $query->addCriterion($filterProp, SupportedOperatorHelper::CONTAIN, $values); |
304 | } |
305 | } |
306 | } |
307 | |
308 | $queryBuilder->setCriteria($query); |
309 | if (isset($options['limit'])) { |
310 | $queryBuilder->setLimit($options['limit']); |
311 | } |
312 | if (isset($options['offset'])) { |
313 | $queryBuilder->setOffset($options['offset']); |
314 | } |
315 | if (isset($options['order'])) { |
316 | $queryBuilder->sort($options['order']); |
317 | } |
318 | return $queryBuilder; |
319 | } |
320 | |
321 | /** |
322 | * @return ComplexSearchService |
323 | */ |
324 | private function getSearchService() |
325 | { |
326 | return $this->getModel()->getSearchInterface(); |
327 | } |
328 | |
329 | /** |
330 | * @param core_kernel_classes_Class $class |
331 | * @return core_kernel_classes_Class[] |
332 | * @throws |
333 | */ |
334 | private function getSubClasses(core_kernel_classes_Class $class) |
335 | { |
336 | $search = $this->getSearchService(); |
337 | $queryBuilder = $search->query(); |
338 | $query = $queryBuilder->newQuery()->add(OntologyRdfs::RDFS_SUBCLASSOF)->equals($class->getUri()); |
339 | $queryBuilder->setCriteria($query); |
340 | //classes always sorted by label |
341 | $order = [RDFS_LABEL => 'asc']; |
342 | $queryBuilder->sort($order); |
343 | $result = []; |
344 | $search->setLanguage($queryBuilder, \common_session_SessionManager::getSession()->getDataLanguage()); |
345 | foreach ($search->getGateway()->search($queryBuilder) as $subclass) { |
346 | $result[] = $this->getClass($subclass->getUri()); |
347 | } |
348 | return $result; |
349 | } |
350 | |
351 | private function mustFilterTranslations(core_kernel_classes_Class $class): bool |
352 | { |
353 | $featureFlagChecker = $this->getFeatureFlagChecker(); |
354 | |
355 | if ($featureFlagChecker->isEnabled('FEATURE_FLAG_TRANSLATION_DEVELOPER_MODE')) { |
356 | return false; |
357 | } |
358 | |
359 | if (!$featureFlagChecker->isEnabled('FEATURE_FLAG_TRANSLATION_ENABLED')) { |
360 | return false; |
361 | } |
362 | |
363 | $parentClassIds = $class->getParentClassesIds(); |
364 | $mainClass = array_pop($parentClassIds); |
365 | |
366 | return in_array( |
367 | $mainClass ?? $class->getUri(), |
368 | [ |
369 | TaoOntology::CLASS_URI_ITEM, |
370 | TaoOntology::CLASS_URI_TEST |
371 | ], |
372 | true |
373 | ); |
374 | } |
375 | |
376 | private function getFeatureFlagChecker(): FeatureFlagCheckerInterface |
377 | { |
378 | return ServiceManager::getServiceManager()->getContainer()->get(FeatureFlagChecker::class); |
379 | } |
380 | } |