"Fossies" - the Fresh Open Source Software Archive 
Member "neos-development-collection-7.0.1/Neos.Neos/Classes/Service/View/NodeView.php" (23 Feb 2021, 14251 Bytes) of package /linux/www/neos-development-collection-7.0.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "NodeView.php" see the
Fossies "Dox" file reference documentation.
1 <?php
2 namespace Neos\Neos\Service\View;
3
4 /*
5 * This file is part of the Neos.Neos package.
6 *
7 * (c) Contributors of the Neos Project - www.neos.io
8 *
9 * This package is Open Source Software. For the full copyright and license
10 * information, please view the LICENSE file which was distributed with this
11 * source code.
12 */
13
14 use Neos\Flow\Annotations as Flow;
15 use Neos\Eel\FlowQuery\FlowQuery;
16 use Neos\Flow\Log\Utility\LogEnvironment;
17 use Neos\Flow\Mvc\View\JsonView;
18 use Neos\Flow\Security\Authorization\PrivilegeManagerInterface;
19 use Neos\Neos\Security\Authorization\Privilege\NodeTreePrivilege;
20 use Neos\ContentRepository\Domain\Model\NodeInterface;
21 use Neos\Utility\Arrays;
22 use Neos\ContentRepository\Security\Authorization\Privilege\Node\NodePrivilegeSubject;
23 use Psr\Log\LoggerInterface;
24
25 /**
26 * An View specialized on single or multiple Nodes in a tree structure
27 *
28 * NOTE: This class only exists for backwards compatibility with not-yet refactored service end points and service
29 * controllers.
30 *
31 * @Flow\Scope("prototype")
32 */
33 class NodeView extends JsonView
34 {
35 /**
36 * @var integer
37 */
38 const STYLE_LIST = 1;
39 const STYLE_TREE = 2;
40
41 /**
42 * @var integer
43 */
44 protected $outputStyle;
45
46 /**
47 * @Flow\Inject
48 * @var LoggerInterface
49 */
50 protected $systemLogger;
51
52 /**
53 * @Flow\Inject
54 * @var PrivilegeManagerInterface
55 */
56 protected $privilegeManager;
57
58 /**
59 * Assigns a node to the NodeView.
60 *
61 * @param NodeInterface $node The node to render
62 * @param array $propertyNames Optional list of property names to include in the JSON output
63 * @return void
64 */
65 public function assignNode(NodeInterface $node, array $propertyNames = ['name', 'path', 'identifier', 'properties', 'nodeType'])
66 {
67 $this->setConfiguration(
68 [
69 'value' => [
70 'data' => [
71 '_only' => ['name', 'path', 'identifier', 'properties', 'nodeType'],
72 '_descend' => ['properties' => $propertyNames]
73 ]
74 ]
75 ]
76 );
77 $this->assign('value', ['data' => $node, 'success' => true]);
78 }
79
80 /**
81 * @param NodeInterface[] $nodes
82 * @throws \Neos\Eel\Exception
83 */
84 public function assignNodes(array $nodes): void
85 {
86 $data = [];
87 foreach ($nodes as $node) {
88 if ($node->getPath() !== '/') {
89 $q = new FlowQuery([$node]);
90 $closestDocumentNode = $q->closest('[instanceof Neos.Neos:Document]')->get(0);
91 if ($closestDocumentNode !== null) {
92 $data[] = [
93 'nodeContextPath' => $node->getContextPath(),
94 'documentNodeContextPath' => $closestDocumentNode->getContextPath(),
95 ];
96 } else {
97 $this->systemLogger->info(sprintf('You have a node that is no longer connected to a parent. Path: %s (Identifier: %s)', $node->getPath(), $node->getIdentifier()), LogEnvironment::fromMethodName(__METHOD__));
98 }
99 }
100 }
101
102 $this->assign('value', ['data' => $data, 'success' => true]);
103 }
104
105 /**
106 * Prepares this view to render a list or tree of child nodes of the given node.
107 *
108 * @param NodeInterface $node The node to fetch child nodes of
109 * @param string $nodeTypeFilter Criteria for filtering the child nodes
110 * @param integer $outputStyle Either STYLE_TREE or STYLE_list
111 * @param integer $depth How many levels of childNodes (0 = unlimited)
112 * @param NodeInterface $untilNode if given, expand all nodes on the rootline towards $untilNode, no matter what is defined with $depth.
113 * @return void
114 */
115 public function assignChildNodes(NodeInterface $node, $nodeTypeFilter, $outputStyle = self::STYLE_LIST, $depth = 0, NodeInterface $untilNode = null)
116 {
117 $this->outputStyle = $outputStyle;
118 $nodes = [];
119 if ($this->privilegeManager->isGranted(NodeTreePrivilege::class, new NodePrivilegeSubject($node))) {
120 $this->collectChildNodeData($nodes, $node, ($nodeTypeFilter === '' ? null : $nodeTypeFilter), $depth, $untilNode);
121 }
122 $this->setConfiguration(['value' => ['data' => ['_descendAll' => []]]]);
123
124 $this->assign('value', ['data' => $nodes, 'success' => true]);
125 }
126
127 /**
128 * Prepares this view to render a list or tree of given node including child nodes.
129 *
130 * @param NodeInterface $node The node to fetch child nodes of
131 * @param string $nodeTypeFilter Criteria for filtering the child nodes
132 * @param integer $depth How many levels of childNodes (0 = unlimited)
133 * @param NodeInterface $untilNode if given, expand all nodes on the rootline towards $untilNode, no matter what is defined with $depth.
134 * @return void
135 */
136 public function assignNodeAndChildNodes(NodeInterface $node, $nodeTypeFilter = '', $depth = 0, NodeInterface $untilNode = null)
137 {
138 $this->outputStyle = self::STYLE_TREE;
139 $data = [];
140 if ($this->privilegeManager->isGranted(NodeTreePrivilege::class, new NodePrivilegeSubject($node))) {
141 $childNodes = [];
142 $this->collectChildNodeData($childNodes, $node, ($nodeTypeFilter === '' ? null : $nodeTypeFilter), $depth, $untilNode);
143 $data = $this->collectTreeNodeData($node, true, $childNodes, $childNodes !== []);
144 }
145 $this->setConfiguration(['value' => ['data' => ['_descendAll' => []]]]);
146
147 $this->assign('value', ['data' => $data, 'success' => true]);
148 }
149
150 /**
151 * Prepares this view to render a list or tree of filtered nodes.
152 *
153 * @param NodeInterface $node
154 * @param array<\Neos\ContentRepository\Domain\Model\NodeData> $matchedNodes
155 * @param integer $outputStyle Either STYLE_TREE or STYLE_list
156 * @return void
157 */
158 public function assignFilteredChildNodes(NodeInterface $node, array $matchedNodes, $outputStyle = self::STYLE_LIST)
159 {
160 $this->outputStyle = $outputStyle;
161 $nodes = $this->collectParentNodeData($node, $matchedNodes);
162 $this->setConfiguration(['value' => ['data' => ['_descendAll' => []]]]);
163
164 $this->assign('value', ['data' => $nodes, 'success' => true]);
165 }
166
167 /**
168 * Collect node data and traverse child nodes
169 *
170 * @param array &$nodes
171 * @param NodeInterface $node
172 * @param string $nodeTypeFilter
173 * @param integer $depth levels of child nodes to fetch. 0 = unlimited
174 * @param \Neos\ContentRepository\Domain\Model\NodeInterface $untilNode if given, expand all nodes on the rootline towards $untilNode, no matter what is defined with $depth.
175 * @param integer $recursionPointer current recursion level
176 * @return void
177 */
178 protected function collectChildNodeData(array &$nodes, NodeInterface $node, $nodeTypeFilter, $depth = 0, NodeInterface $untilNode = null, $recursionPointer = 1)
179 {
180 foreach ($node->getChildNodes($nodeTypeFilter) as $childNode) {
181 if (!$this->privilegeManager->isGranted(NodeTreePrivilege::class, new NodePrivilegeSubject($childNode))) {
182 continue;
183 }
184 /** @var NodeInterface $childNode */
185 $expand = ($depth === 0 || $recursionPointer < $depth);
186
187 if ($expand === false && $untilNode !== null && strpos($untilNode->getPath(), $childNode->getPath()) === 0 && $childNode !== $untilNode) {
188 // in case $untilNode is set, and the current childNode is on the rootline of $untilNode (and not the node itself), expand the node.
189 $expand = true;
190 }
191
192 switch ($this->outputStyle) {
193 case self::STYLE_LIST:
194 $nodeType = $childNode->getNodeType()->getName();
195 $properties = $childNode->getProperties();
196 $properties['__contextNodePath'] = $childNode->getContextPath();
197 $properties['__workspaceName'] = $childNode->getWorkspace()->getName();
198 $properties['__nodeName'] = $childNode->getName();
199 $properties['__nodeType'] = $nodeType;
200 $properties['__title'] = $nodeType === 'Neos.Neos:Document' ? $childNode->getProperty('title') : $childNode->getLabel();
201 array_push($nodes, $properties);
202 if ($expand) {
203 $this->collectChildNodeData($nodes, $childNode, $nodeTypeFilter, $depth, $untilNode, ($recursionPointer + 1));
204 }
205 break;
206 case self::STYLE_TREE:
207 $children = [];
208 $hasChildNodes = $childNode->hasChildNodes($nodeTypeFilter) === true;
209 if ($expand && $hasChildNodes) {
210 $this->collectChildNodeData($children, $childNode, $nodeTypeFilter, $depth, $untilNode, ($recursionPointer + 1));
211 }
212 array_push($nodes, $this->collectTreeNodeData($childNode, $expand, $children, $hasChildNodes));
213 }
214 }
215 }
216
217 /**
218 * @param NodeInterface $rootNode
219 * @param array<\Neos\ContentRepository\Domain\Model\NodeData> $nodes
220 * @return array
221 */
222 public function collectParentNodeData(NodeInterface $rootNode, array $nodes)
223 {
224 $nodeCollection = [];
225
226 $addNode = function ($node, $matched) use ($rootNode, &$nodeCollection) {
227 /** @var NodeInterface $node */
228 $path = str_replace('/', '.children.', substr($node->getPath(), strlen($rootNode->getPath()) + 1));
229 if ($path !== '') {
230 $nodeCollection = Arrays::setValueByPath($nodeCollection, $path . '.node', $node);
231 if ($matched === true) {
232 $nodeCollection = Arrays::setValueByPath($nodeCollection, $path . '.matched', true);
233 }
234 }
235 };
236
237 $findParent = function ($node) use (&$findParent, &$addNode) {
238 /** @var NodeInterface $node */
239 $parent = $node->getParent();
240 if ($parent !== null) {
241 if ($this->privilegeManager->isGranted(NodeTreePrivilege::class, new NodePrivilegeSubject($parent))) {
242 $addNode($parent, false);
243 $findParent($parent);
244 }
245 }
246 };
247
248 foreach ($nodes as $node) {
249 if ($this->privilegeManager->isGranted(NodeTreePrivilege::class, new NodePrivilegeSubject($node))) {
250 $addNode($node, true);
251 $findParent($node);
252 }
253 }
254
255 $treeNodes = [];
256 $self = $this;
257 $collectTreeNodeData = function (&$treeNodes, $node) use (&$collectTreeNodeData, $self) {
258 $children = [];
259 if (isset($node['children'])) {
260 foreach ($node['children'] as $childNode) {
261 $collectTreeNodeData($children, $childNode);
262 }
263 }
264 $treeNodes[] = $self->collectTreeNodeData($node['node'], true, $children, $children !== [], isset($node['matched']));
265 };
266
267 foreach ($nodeCollection as $firstLevelNode) {
268 $collectTreeNodeData($treeNodes, $firstLevelNode);
269 }
270
271 return $treeNodes;
272 }
273
274 /**
275 * @param NodeInterface $node
276 * @param boolean $expand
277 * @param array $children
278 * @param boolean $hasChildNodes
279 * @param boolean $matched
280 * @return array
281 */
282 public function collectTreeNodeData(NodeInterface $node, $expand = true, array $children = [], $hasChildNodes = false, $matched = false)
283 {
284 $isTimedPage = false;
285 $now = new \DateTime();
286 $now = $now->getTimestamp();
287 $hiddenBeforeDateTime = $node->getHiddenBeforeDateTime();
288 $hiddenAfterDateTime = $node->getHiddenAfterDateTime();
289
290 if ($hiddenBeforeDateTime !== null && $hiddenBeforeDateTime->getTimestamp() > $now) {
291 $isTimedPage = true;
292 }
293 if ($hiddenAfterDateTime !== null) {
294 $isTimedPage = true;
295 }
296
297 $classes = [];
298 if ($isTimedPage === true && $node->isHidden() === false) {
299 array_push($classes, 'neos-timedVisibility');
300 }
301 if ($node->isHidden() === true) {
302 array_push($classes, 'neos-hidden');
303 }
304 if ($node->isHiddenInIndex() === true) {
305 array_push($classes, 'neos-hiddenInIndex');
306 }
307 if ($matched) {
308 array_push($classes, 'neos-matched');
309 }
310
311 $uriBuilder = $this->controllerContext->getUriBuilder();
312 $nodeType = $node->getNodeType();
313 $nodeTypeConfiguration = $nodeType->getFullConfiguration();
314 if ($node->getNodeType()->isOfType('Neos.Neos:Document')) {
315 $uriForNode = $uriBuilder->reset()->setFormat('html')->setCreateAbsoluteUri(true)->uriFor('show', ['node' => $node], 'Frontend\Node', 'Neos.Neos');
316 } else {
317 $uriForNode = '#';
318 }
319 $label = $node->getLabel();
320 $nodeTypeLabel = $node->getNodeType()->getLabel();
321 $treeNode = [
322 'key' => $node->getContextPath(),
323 'title' => $label,
324 'fullTitle' => $node->getProperty('title'),
325 'nodeTypeLabel' => $nodeTypeLabel,
326 'tooltip' => '', // will be filled on the client side, because nodeTypeLabel contains the localization string instead of the localized value
327 'href' => $uriForNode,
328 'isFolder' => $hasChildNodes,
329 'isLazy' => ($hasChildNodes && !$expand),
330 'nodeType' => $nodeType->getName(),
331 'isAutoCreated' => $node->isAutoCreated(),
332 'expand' => $expand,
333 'addClass' => implode(' ', $classes),
334 'name' => $node->getName(),
335 'iconClass' => isset($nodeTypeConfiguration['ui']) && isset($nodeTypeConfiguration['ui']['icon']) ? $nodeTypeConfiguration['ui']['icon'] : '',
336 'isHidden' => $node->isHidden()
337 ];
338 if ($hasChildNodes) {
339 $treeNode['children'] = $children;
340 }
341 return $treeNode;
342 }
343 }