"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 }