/** * Fetches the site with the given name and exports it into XML. * * @param array<\TYPO3\TYPO3\Domain\Model\Site> $sites * @return void */ public function export(array $sites) { $this->nodeRepository->getContext()->setInvisibleContentShown(TRUE); $this->nodeRepository->getContext()->setInaccessibleContentShown(TRUE); $xmlWriter = new \XMLWriter(); $xmlWriter->openUri('php://output'); $xmlWriter->startDocument('1.0', 'UTF-8'); $xmlWriter->startElement('root'); foreach ($sites as $site) { $xmlWriter->startElement('site'); // site attributes $xmlWriter->writeAttribute('nodeName', $site->getNodeName()); // site properties $xmlWriter->startElement('properties'); $xmlWriter->writeElement('name', $site->getName()); $xmlWriter->writeElement('state', $site->getState()); $xmlWriter->writeElement('siteResourcesPackageKey', $site->getSiteResourcesPackageKey()); $xmlWriter->endElement(); // on to the nodes... $node = $this->nodeRepository->getContext()->getNode('/Sites/' . $site->getNodeName()); foreach ($node->getChildNodes() as $childNode) { $this->exportNode($childNode, $xmlWriter); } $xmlWriter->endElement(); } $xmlWriter->endElement(); $xmlWriter->endDocument(); $xmlWriter->flush(); }
/** * Is it possile to render $node with $typoScriptPath? * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @param string $typoScriptPath * @return boolean TRUE if $node can be rendered at $typoScriptPath */ public function canRenderWithNodeAndPath(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node, $typoScriptPath) { $currentSiteNode = $this->nodeRepository->getContext()->getCurrentSiteNode(); // TODO: find closest folder node from this node... $closestFolderNode = $node; $typoScriptConfiguration = $this->typoScriptService->getMergedTypoScriptObjectTree($currentSiteNode, $closestFolderNode); $typoScriptRuntime = new \TYPO3\TypoScript\Core\Runtime($typoScriptConfiguration, $this->controllerContext); return $typoScriptRuntime->canRender($typoScriptPath); }
/** * Render the Uri. * * @param mixed $node A node object or a node path * @param string $format Format to use for the URL, for example "html" or "json" * @param boolean $absolute If set, an absolute URI is rendered * @return string The rendered URI */ public function render($node = NULL, $format = NULL, $absolute = FALSE) { $request = $this->controllerContext->getRequest()->getMainRequest(); $uriBuilder = new \TYPO3\FLOW3\Mvc\Routing\UriBuilder(); $uriBuilder->setRequest($request); if ($node === NULL) { $node = $this->nodeRepository->getContext()->getCurrentNode(); } if ($format === NULL) { $format = $request->getFormat(); } $uri = $uriBuilder->reset()->setCreateAbsoluteUri($absolute)->setFormat($format)->uriFor('show', array('node' => $node), 'Frontend\\Node', 'TYPO3.TYPO3'); return $uri; }
/** * Checks, whether given value is a Node object and if so, sets $this->value to the respective node context path. * * In order to render a suitable frontend URI, this function strips off the path to the site node and only keeps * the actual node path relative to that site node. In practice this function would set $this->value as follows: * * absolute node path: /sites/footypo3org/homepage/about * $this->value: homepage/about * * absolute node path: /sites/footypo3org/homepage/about@user-admin * $this->value: homepage/about@user-admin * * @param mixed $value Either a Node object or an absolute context node path * @return boolean TRUE if value could be resolved successfully, otherwise FALSE. */ protected function resolveValue($value) { if (!$value instanceof NodeInterface && !is_string($value)) { return FALSE; } if (is_string($value)) { preg_match(NodeInterface::MATCH_PATTERN_CONTEXTPATH, $value, $matches); if (!isset($matches['NodePath'])) { return FALSE; } $contentContext = $this->nodeRepository->getContext(); if ($contentContext->getWorkspace(FALSE) === NULL) { return FALSE; } $node = $contentContext->getCurrentSiteNode()->getNode($matches['NodePath']); } else { $node = $value; $contentContext = $this->nodeRepository->getContext(); } if ($node instanceof NodeInterface) { while ($node->getContentType()->isOfType('TYPO3.Phoenix.ContentTypes:Shortcut')) { $childNodes = $node->getChildNodes('TYPO3.Phoenix.ContentTypes:Page,TYPO3.Phoenix.ContentTypes:Shortcut'); $node = current($childNodes); } $nodeContextPath = $node->getContextPath(); $siteNodePath = $contentContext->getCurrentSiteNode()->getPath(); } else { return FALSE; } if (substr($nodeContextPath, 0, strlen($siteNodePath)) !== $siteNodePath) { return FALSE; } $this->value = substr($nodeContextPath, strlen($siteNodePath) + 1); return TRUE; }
/** * Returns the menu items according to the defined settings. * * @return array */ public function getItems() { if ($this->items === NULL) { $this->items = $this->buildItems($this->nodeRepository->getContext()); } return $this->items; }
/** * {@inheritdoc} * * @param \TYPO3\Eel\FlowQuery\FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return mixed|null if the operation is final, the return value */ public function evaluate(\TYPO3\Eel\FlowQuery\FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { $siteNode = $this->nodeRepository->getContext()->getCurrentSiteNode(); while ($contextNode->getParent() !== $siteNode) { $contextNode = $contextNode->getParent(); if (!isset($outputNodePaths[$contextNode->getPath()])) { $output[] = $contextNode; $outputNodePaths[$contextNode->getPath()] = TRUE; } } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * Returns the initialized node that is referenced by $relativeContextNodePath * * @param string $relativeContextNodePath * @return \TYPO3\TYPO3CR\Domain\Model\NodeInterface * @throws \TYPO3\TYPO3\Routing\Exception\NoWorkspaceException * @throws \TYPO3\TYPO3\Routing\Exception\NoSiteException * @throws \TYPO3\TYPO3\Routing\Exception\NoSuchNodeException * @throws \TYPO3\TYPO3\Routing\Exception\NoSiteNodeException * @throws \TYPO3\TYPO3\Routing\Exception\InvalidRequestPathException */ public function getNodeByContextNodePath($relativeContextNodePath) { if ($relativeContextNodePath !== '') { preg_match(NodeInterface::MATCH_PATTERN_CONTEXTPATH, $relativeContextNodePath, $matches); if (!isset($matches['NodePath'])) { throw new Exception\InvalidRequestPathException('The request path "' . $relativeContextNodePath . '" is not valid', 1346949309); } $relativeNodePath = $matches['NodePath']; } else { $relativeNodePath = ''; } if ($this->nodeRepository->getContext() === NULL) { $workspaceName = isset($matches['WorkspaceName']) ? $matches['WorkspaceName'] : 'live'; $contentContext = new ContentContext($workspaceName); $contentContext->setInvisibleContentShown(TRUE); $this->nodeRepository->setContext($contentContext); } else { $contentContext = $this->nodeRepository->getContext(); } $workspace = $contentContext->getWorkspace(FALSE); if (!$workspace) { throw new Exception\NoWorkspaceException('No workspace found for request path "' . $relativeContextNodePath . '"', 1346949318); } $site = $contentContext->getCurrentSite(); if (!$site) { throw new Exception\NoSiteException('No site found for request path "' . $relativeContextNodePath . '"', 1346949693); } $siteNode = $contentContext->getCurrentSiteNode(); if (!$siteNode) { throw new Exception\NoSiteNodeException('No site node found for request path "' . $relativeContextNodePath . '"', 1346949728); } $currentAccessModeFromContext = $contentContext->isInaccessibleContentShown(); $contentContext->setInaccessibleContentShown(TRUE); $node = $relativeNodePath === '' ? $siteNode->getPrimaryChildNode() : $siteNode->getNode($relativeNodePath); $contentContext->setInaccessibleContentShown($currentAccessModeFromContext); if (!$node instanceof NodeInterface) { throw new Exception\NoSuchNodeException('No node found on request path "' . $relativeContextNodePath . '"', 1346949857); } return $node; }
/** * Returns a merged TypoScript object tree in the context of the given nodes * * The start node and end node mark the starting point and end point of the * path to take while searching for TypoScript configuration. The path of the * start node must be the base path of the end node's path. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $startNode Node marking the starting point * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $endNode Node marking the end point * @return array The merged object tree as of the given node */ public function getMergedTypoScriptObjectTree(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $startNode, \TYPO3\TYPO3CR\Domain\Model\NodeInterface $endNode) { $contentContext = $this->nodeRepository->getContext(); $parentNodes = $contentContext->getNodesOnPath($startNode->getPath(), $endNode->getPath()); if (!is_array($parentNodes)) { return NULL; } $siteResourcesPackageKey = $contentContext->getCurrentSite()->getSiteResourcesPackageKey(); $typoScriptsPath = sprintf($this->typoScriptsPathPattern, $siteResourcesPackageKey); $mergedTypoScriptCode = $this->readExternalTypoScriptFile('resource://TYPO3.TYPO3/Private/DefaultTypoScript/All.ts2'); $mergedTypoScriptCode .= $this->readExternalTypoScriptFile($typoScriptsPath . '/Library/Root.ts2'); $currentTypoScriptPath = $typoScriptsPath . '/Nodes'; foreach ($parentNodes as $node) { $nodeName = $node->getName(); $mergedTypoScriptCode .= Files::getFileContents($this->getMixedCasedPathAndFilename($currentTypoScriptPath . '/' . $nodeName . '.ts2')) . chr(10); $currentTypoScriptPath .= '/' . basename($this->getMixedCasedPathAndFilename($currentTypoScriptPath . '/' . $nodeName)); $typoScriptNodes = $node->getChildNodes('TYPO3.TYPO3:TypoScript'); foreach ($typoScriptNodes as $typoScriptNode) { $mergedTypoScriptCode .= $typoScriptNode->getProperty('sourceCode') . chr(10); } } return $this->typoScriptParser->parse($mergedTypoScriptCode, $typoScriptsPath); }
/** * @param string $pathAndFilename * @return void * @throws \TYPO3\FLOW3\Package\Exception\UnknownPackageException * @throws \TYPO3\FLOW3\Package\Exception\InvalidPackageStateException */ public function importSitesFromFile($pathAndFilename) { $contentContext = $this->nodeRepository->getContext(); $contentContext->setInvisibleContentShown(TRUE); $contentContext->setInaccessibleContentShown(TRUE); // no file_get_contents here because it does not work on php://stdin $fp = fopen($pathAndFilename, 'rb'); $xmlString = ''; while (!feof($fp)) { $xmlString .= fread($fp, 4096); } fclose($fp); $xml = new \SimpleXMLElement($xmlString); foreach ($xml->site as $siteXml) { $site = $this->siteRepository->findOneByNodeName((string) $siteXml['nodeName']); if ($site === NULL) { $site = new \TYPO3\TYPO3\Domain\Model\Site((string) $siteXml['nodeName']); $this->siteRepository->add($site); } else { $this->siteRepository->update($site); } $site->setName((string) $siteXml->properties->name); $site->setState((int) $siteXml->properties->state); $siteResourcesPackageKey = (string) $siteXml->properties->siteResourcesPackageKey; if ($this->packageManager->isPackageAvailable($siteResourcesPackageKey) === FALSE) { throw new \TYPO3\FLOW3\Package\Exception\UnknownPackageException('Package "' . $siteResourcesPackageKey . '" specified in the XML as site resources package does not exist.', 1303891443); } if ($this->packageManager->isPackageActive($siteResourcesPackageKey) === FALSE) { throw new \TYPO3\FLOW3\Package\Exception\InvalidPackageStateException('Package "' . $siteResourcesPackageKey . '" specified in the XML as site resources package is not active.', 1303898135); } $site->setSiteResourcesPackageKey($siteResourcesPackageKey); $rootNode = $contentContext->getWorkspace()->getRootNode(); if ($rootNode->getNode('/sites') === NULL) { $rootNode->createSingleNode('sites'); } $siteNode = $rootNode->getNode('/sites/' . $site->getNodeName()); if ($siteNode === NULL) { $siteNode = $rootNode->getNode('/sites')->createSingleNode($site->getNodeName()); } $siteNode->setContentObject($site); $this->parseNodes($siteXml, $siteNode); } }
/** * Shows the specified node and takes visibility and access restrictions into * account. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @return string View output for the specified node */ public function showWireframeAction(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node) { if (!$node->isAccessible()) { try { $this->authenticationManager->authenticate(); } catch (\Exception $exception) { } } if (!$node->isAccessible() && !$this->nodeRepository->getContext()->isInaccessibleContentShown()) { $this->throwStatus(403); } if (!$node->isVisible() && !$this->nodeRepository->getContext()->isInvisibleContentShown()) { $this->throwStatus(404); } if ($node->getContentType() === 'TYPO3.Phoenix.ContentTypes:Shortcut') { $this->view->assign('wireframeMode', $node); } $this->nodeRepository->getContext()->setCurrentNode($node); $this->view->assign('value', $node); $this->view->setTypoScriptPath('wireframeMode'); $this->response->setHeader('Cache-Control', 'public, s-maxage=600', FALSE); }
/** * @return \TYPO3\FLOW3\Persistence\QueryResultInterface */ public function getCurrentWorkspace() { return $this->nodeRepository->getContext()->getWorkspace(FALSE); }
/** * Converts the specified node path into a Node. * * The node path must be an absolute context node path and can be specified as a string or as an array item with the * key "__contextNodePath". The latter case is for updating existing nodes. * * This conversion method does not support / allow creation of new nodes because new nodes should be created through * the createNode() method of an existing reference node. * * Also note that the context's "current node" is not affected by this object converter, you will need to set it to * whatever node your "current" node is, if any. * * All elements in the source array which start with two underscores (like __contextNodePath) are specially treated * by this converter. * * All elements in the source array which start with a *single underscore (like _hidden) are *directly* set on the Node * object. * * All other elements, not being prefixed with underscore, are properties of the node. * * @param string|array $source Either a string or array containing the absolute context node path which identifies the node. For example "/sites/mysitecom/homepage/about@user-admin" * @param string $targetType not used * @param array $subProperties not used * @param \TYPO3\FLOW3\Property\PropertyMappingConfigurationInterface $configuration not used * @return mixed An object or \TYPO3\FLOW3\Error\Error if the input format is not supported or could not be converted for other reasons * @throws \Exception */ public function convertFrom($source, $targetType, array $subProperties = array(), \TYPO3\FLOW3\Property\PropertyMappingConfigurationInterface $configuration = NULL) { if (is_string($source)) { $source = array('__contextNodePath' => $source); } if (!is_array($source) || !isset($source['__contextNodePath'])) { return new Error('Could not convert ' . gettype($source) . ' to Node object, a valid absolute context node path as a string or array is expected.', 1302879936); } preg_match(NodeInterface::MATCH_PATTERN_CONTEXTPATH, $source['__contextNodePath'], $matches); if (!isset($matches['NodePath'])) { return new Error('Could not convert array to Node object because the node path was invalid.', 1285162903); } $nodePath = $matches['NodePath']; if ($this->nodeRepository->getContext() === NULL) { $workspaceName = isset($matches['WorkspaceName']) ? $matches['WorkspaceName'] : 'live'; $contentContext = new ContentContext($workspaceName); $this->nodeRepository->setContext($contentContext); } else { $contentContext = $this->nodeRepository->getContext(); $workspaceName = $contentContext->getWorkspace()->getName(); } if ($workspaceName !== 'live') { $contentContext->setInvisibleContentShown(TRUE); if ($configuration->getConfigurationValue('TYPO3\\TYPO3\\Routing\\NodeObjectConverter', self::REMOVED_CONTENT_SHOWN) === TRUE) { $contentContext->setRemovedContentShown(TRUE); } } $workspace = $contentContext->getWorkspace(FALSE); if (!$workspace) { return new Error(sprintf('Could not convert %s to Node object because the workspace "%s" as specified in the context node path does not exist.', $source['__contextNodePath'], $workspaceName), 1285162905); } $currentAccessModeFromContext = $contentContext->isInaccessibleContentShown(); $contentContext->setInaccessibleContentShown(TRUE); $node = $contentContext->getNode($nodePath); $contentContext->setInaccessibleContentShown($currentAccessModeFromContext); if (!$node) { return new Error(sprintf('Could not convert array to Node object because the node "%s" does not exist.', $nodePath), 1285162908); } $contentTypeProperties = $node->getContentType()->getProperties(); foreach ($source as $nodePropertyKey => $nodePropertyValue) { if (substr($nodePropertyKey, 0, 2) === '__') { continue; } if ($nodePropertyKey[0] === '_') { $propertyName = substr($nodePropertyKey, 1); // TODO: Hack: we need to create DateTime objects for some properties of Node if (($propertyName === 'hiddenBeforeDateTime' || $propertyName === 'hiddenAfterDateTime') && is_string($nodePropertyValue)) { if ($nodePropertyValue !== '') { $nodePropertyValue = \DateTime::createFromFormat('!Y-m-d', $nodePropertyValue); } else { $nodePropertyValue = NULL; } } \TYPO3\FLOW3\Reflection\ObjectAccess::setProperty($node, $propertyName, $nodePropertyValue); } else { if (!isset($contentTypeProperties[$nodePropertyKey])) { throw new \Exception('TODO: content type XY does not have a property YY according to the schema'); } if (isset($contentTypeProperties[$nodePropertyKey]['type'])) { $targetType = $contentTypeProperties[$nodePropertyKey]['type']; if ($this->objectManager->isRegistered($targetType)) { $nodePropertyValue = $this->propertyMapper->convert(json_decode($nodePropertyValue, TRUE), $targetType); } } $node->setProperty($nodePropertyKey, $nodePropertyValue); } } return $node; }
/** * Wrap the $content identified by $node with the needed markup for * the backend. * $parameters can be used to further pass parameters to the content element. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @param string $typoscriptPath * @param string $content * @param boolean $isPage * @return string */ public function wrapContentObject(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node, $typoscriptPath, $content, $isPage = FALSE) { $contentType = $node->getContentType(); $tagBuilder = new \TYPO3\Fluid\Core\ViewHelper\TagBuilder('div'); $tagBuilder->forceClosingTag(TRUE); if (!$node->isRemoved()) { $tagBuilder->setContent($content); } if (!$isPage) { $cssClasses = array('t3-contentelement'); $cssClasses[] = str_replace(array(':', '.'), '-', strtolower($contentType->getName())); if ($node->isHidden()) { $cssClasses[] = 't3-contentelement-hidden'; } if ($node->isRemoved()) { $cssClasses[] = 't3-contentelement-removed'; } $tagBuilder->addAttribute('class', implode(' ', $cssClasses)); $tagBuilder->addAttribute('id', 'c' . $node->getIdentifier()); } try { $this->accessDecisionManager->decideOnResource('TYPO3_TYPO3_Backend_BackendController'); } catch (\TYPO3\FLOW3\Security\Exception\AccessDeniedException $e) { return $tagBuilder->render(); } $tagBuilder->addAttribute('typeof', 'typo3:' . $contentType->getName()); $tagBuilder->addAttribute('about', $node->getContextPath()); $this->addScriptTag($tagBuilder, '__workspacename', $node->getWorkspace()->getName()); $this->addScriptTag($tagBuilder, '_removed', $node->isRemoved() ? 'true' : 'false', 'boolean'); $this->addScriptTag($tagBuilder, '_typoscriptPath', $typoscriptPath); foreach ($contentType->getProperties() as $propertyName => $propertyConfiguration) { $dataType = isset($propertyConfiguration['type']) ? $propertyConfiguration['type'] : 'string'; if ($propertyName[0] === '_') { $propertyValue = \TYPO3\FLOW3\Reflection\ObjectAccess::getProperty($node, substr($propertyName, 1)); } else { $propertyValue = $node->getProperty($propertyName); } // Serialize boolean values to String if (isset($propertyConfiguration['type']) && $propertyConfiguration['type'] === 'boolean') { $propertyValue = $propertyValue ? 'true' : 'false'; } // Serialize date values to String if ($propertyValue !== NULL && isset($propertyConfiguration['type']) && $propertyConfiguration['type'] === 'date') { $propertyValue = $propertyValue->format('Y-m-d'); } // Serialize objects to JSON strings if (is_object($propertyValue) && $propertyValue !== NULL && isset($propertyConfiguration['type']) && $this->objectManager->isRegistered($propertyConfiguration['type'])) { $gettableProperties = \TYPO3\FLOW3\Reflection\ObjectAccess::getGettableProperties($propertyValue); $convertedProperties = array(); foreach ($gettableProperties as $key => $value) { if (is_object($value)) { $entityIdentifier = $this->persistenceManager->getIdentifierByObject($value); if ($entityIdentifier !== NULL) { $value = $entityIdentifier; } } $convertedProperties[$key] = $value; } $propertyValue = json_encode($convertedProperties); $dataType = 'jsonEncoded'; } $this->addScriptTag($tagBuilder, $propertyName, $propertyValue, $dataType); } if (!$isPage) { // add CSS classes $this->addScriptTag($tagBuilder, '__contenttype', $contentType->getName()); } else { $tagBuilder->addAttribute('id', 't3-page-metainformation'); $tagBuilder->addAttribute('data-__sitename', $this->nodeRepository->getContext()->getCurrentSite()->getName()); $tagBuilder->addAttribute('data-__siteroot', sprintf('/sites/%s@%s', $this->nodeRepository->getContext()->getCurrentSite()->getNodeName(), $this->nodeRepository->getContext()->getWorkspace()->getName())); } return $tagBuilder->render(); }