/** * Index all nodes. * * This command (re-)indexes all nodes contained in the content repository and sets the schema beforehand. * * * @param string $workspace * @return void */ public function buildCommand($workspace = NULL) { $this->indexedNodes = 0; if ($workspace === NULL) { foreach ($this->workspaceRepository->findAll() as $workspace) { $this->indexWorkspace($workspace->getName()); } } else { $this->indexWorkspace($workspace); } $this->outputLine('Finished indexing.'); }
/** * Shows a list of existing workspaces * * @return string */ public function indexAction() { $user = $this->userService->getCurrentUser(); $workspacesArray = []; /** @var Workspace $workspace */ foreach ($this->workspaceRepository->findAll() as $workspace) { // FIXME: This check should be implemented through a specialized Workspace Privilege or something similar if ($workspace->getOwner() !== null && $workspace->getOwner() !== $user) { continue; } $workspaceArray = ['name' => $workspace->getName(), 'title' => $workspace->getTitle(), 'description' => $workspace->getDescription(), 'baseWorkspace' => $workspace->getBaseWorkspace()]; if ($user !== null) { $workspaceArray['readonly'] = !$this->userService->currentUserCanPublishToWorkspace($workspace); } $workspacesArray[] = $workspaceArray; } $this->view->assign('workspaces', $workspacesArray); }
/** * Creates an array of workspace names and their respective titles which are possible base workspaces for other * workspaces. * * @param Workspace $excludedWorkspace If set, this workspace will be excluded from the list of returned workspaces * @return array */ protected function prepareBaseWorkspaceOptions(Workspace $excludedWorkspace = null) { $baseWorkspaceOptions = []; foreach ($this->workspaceRepository->findAll() as $workspace) { /** @var Workspace $workspace */ if (!$workspace->isPersonalWorkspace() && $workspace !== $excludedWorkspace && ($workspace->isPublicWorkspace() || $workspace->isInternalWorkspace() || $this->userService->currentUserCanManageWorkspace($workspace))) { $baseWorkspaceOptions[$workspace->getName()] = $workspace->getTitle(); } } return $baseWorkspaceOptions; }
/** * @param NodeInterface $node * @return void */ protected function indexAllNodeVariants(NodeInterface $node) { $nodeIdentifier = $node->getIdentifier(); $allIndexedVariants = $this->indexClient->query('SELECT __identifier__ FROM objects WHERE __identifier = "' . $nodeIdentifier . '"'); foreach ($allIndexedVariants as $nodeVariant) { $this->indexClient->removeData($nodeVariant['__identifier__']); } foreach ($this->workspaceRepository->findAll() as $workspace) { $this->indexNodeInWorkspace($nodeIdentifier, $workspace->getName()); } }
/** * Detect and fix nodes in non-live workspaces whose identifier does not match their corresponding node in the * live workspace. * * @param string $workspaceName This argument will be ignored * @param boolean $dryRun Simulate? * @return void */ public function fixNodesWithInconsistentIdentifier($workspaceName, $dryRun) { $this->output->outputLine('Checking for nodes with inconsistent identifier ...'); $nodesArray = []; $liveWorkspaceNames = []; $nonLiveWorkspaceNames = []; foreach ($this->workspaceRepository->findAll() as $workspace) { /** @var Workspace $workspace */ if ($workspace->getBaseWorkspace() !== null) { $nonLiveWorkspaceNames[] = $workspace->getName(); } else { $liveWorkspaceNames[] = $workspace->getName(); } } foreach ($nonLiveWorkspaceNames as $workspaceName) { /** @var QueryBuilder $queryBuilder */ $queryBuilder = $this->entityManager->createQueryBuilder(); $queryBuilder->select('nonlive.Persistence_Object_Identifier, nonlive.identifier, nonlive.path, live.identifier AS liveIdentifier')->from(NodeData::class, 'nonlive')->join(NodeData::class, 'live', 'WITH', 'live.path = nonlive.path AND live.dimensionsHash = nonlive.dimensionsHash AND live.identifier != nonlive.identifier')->where('nonlive.workspace = ?1')->andWhere($queryBuilder->expr()->in('live.workspace', $liveWorkspaceNames))->andWhere('nonlive.path != \'/\'')->setParameter(1, $workspaceName); foreach ($queryBuilder->getQuery()->getArrayResult() as $nodeDataArray) { $this->output->outputLine('Node %s in workspace %s has identifier %s but live node has identifier %s.', [$nodeDataArray['path'], $workspaceName, $nodeDataArray['identifier'], $nodeDataArray['liveIdentifier']]); $nodesArray[] = $nodeDataArray; } } if ($nodesArray === []) { return; } if (!$dryRun) { $self = $this; $this->output->outputLine(); $this->output->outputLine('Nodes with inconsistent identifiers found.'); $this->askBeforeExecutingTask(sprintf('Do you want to fix the identifiers of %s node%s now?', count($nodesArray), count($nodesArray) > 1 ? 's' : ''), function () use($self, $nodesArray) { foreach ($nodesArray as $nodeArray) { /** @var QueryBuilder $queryBuilder */ $queryBuilder = $this->entityManager->createQueryBuilder(); $queryBuilder->update(NodeData::class, 'nonlive')->set('nonlive.identifier', $queryBuilder->expr()->literal($nodeArray['liveIdentifier']))->where('nonlive.Persistence_Object_Identifier = ?1')->setParameter(1, $nodeArray['Persistence_Object_Identifier']); $result = $queryBuilder->getQuery()->getResult(); if ($result !== 1) { $self->output->outputLine('<error>Error:</error> The update query returned an unexpected result!'); $self->output->outputLine('<b>Query:</b> ' . $queryBuilder->getQuery()->getSQL()); $self->output->outputLine('<b>Result:</b> %s', [var_export($result, true)]); exit(1); } } $self->output->outputLine('Fixed inconsistent identifiers.'); }); } else { $this->output->outputLine('Found %s node%s with inconsistent identifiers which need to be fixed.', array(count($nodesArray), count($nodesArray) > 1 ? 's' : '')); } $this->output->outputLine(); }
/** * Display a list of existing workspaces * * @return void */ public function listCommand() { $workspaces = $this->workspaceRepository->findAll(); if ($workspaces->count() === 0) { $this->outputLine('No workspaces found.'); $this->quit(0); } $tableRows = array(); $headerRow = array('Name', 'Base Workspace', 'Title', 'Description'); foreach ($workspaces as $workspace) { $tableRows[] = array($workspace->getName(), $workspace->getBaseWorkspace() ? $workspace->getBaseWorkspace()->getName() : '', $workspace->getTitle(), $workspace->getDescription()); } $this->output->outputTable($tableRows, $headerRow); }
/** * Collects all nodes of the given node type which refer to an invalid workspace * configuration. * * Note: due to the foreign key constraints in the database, there actually never should * be any node with n.workspace of a non-existing workspace because if that workspace * does not exist anymore, the value would turn NULL. But the query covers this nevertheless. * Better safe than sorry. * * @return array */ protected function collectNodesWithInvalidWorkspace() { $nodes = []; $workspaceNames = []; foreach ($this->workspaceRepository->findAll() as $workspace) { $workspaceNames[] = $workspace->getName(); } /** @var QueryBuilder $queryBuilder */ $queryBuilder = $this->entityManager->createQueryBuilder(); $queryBuilder->select('n')->from('TYPO3\\TYPO3CR\\Domain\\Model\\NodeData', 'n')->add('where', $queryBuilder->expr()->orX($queryBuilder->expr()->notIn('n.workspace', $workspaceNames), $queryBuilder->expr()->isNull('n.workspace'))); foreach ($queryBuilder->getQuery()->getArrayResult() as $nodeDataArray) { $this->output->outputLine('Node %s (identifier: %s) refers to an invalid workspace: %s', [$nodeDataArray['path'], $nodeDataArray['identifier'], isset($nodeDataArray['workspace']) ? $nodeDataArray['workspace'] : 'null']); $nodes[] = $nodeDataArray; } return $nodes; }
/** * Index all nodes by creating a new index and when everything was completed, switch the index alias. * * This command (re-)indexes all nodes contained in the content repository and sets the schema beforehand. * * @param integer $limit Amount of nodes to index at maximum * @param boolean $update if TRUE, do not throw away the index at the start. Should *only be used for development*. * @param string $workspace name of the workspace which should be indexed * @param string $postfix Index postfix, index with the same postifix will be deleted if exist * @return void */ public function buildCommand($limit = null, $update = false, $workspace = null, $postfix = null) { if ($update === true) { $this->logger->log('!!! Update Mode (Development) active!', LOG_INFO); } else { $this->nodeIndexer->setIndexNamePostfix($postfix ?: time()); if ($this->nodeIndexer->getIndex()->exists() === true) { $this->logger->log(sprintf('Deleted index with the same postfix (%s)!', $postfix), LOG_WARNING); $this->nodeIndexer->getIndex()->delete(); } $this->nodeIndexer->getIndex()->create(); $this->logger->log('Created index ' . $this->nodeIndexer->getIndexName(), LOG_INFO); $nodeTypeMappingCollection = $this->nodeTypeMappingBuilder->buildMappingInformation($this->nodeIndexer->getIndex()); foreach ($nodeTypeMappingCollection as $mapping) { /** @var \Flowpack\ElasticSearch\Domain\Model\Mapping $mapping */ $mapping->apply(); } $this->logger->log('Updated Mapping.', LOG_INFO); } $this->logger->log(sprintf('Indexing %snodes ... ', $limit !== null ? 'the first ' . $limit . ' ' : ''), LOG_INFO); $count = 0; if ($workspace === null && $this->settings['indexAllWorkspaces'] === false) { $workspace = 'live'; } $callback = function ($workspaceName, $indexedNodes, $dimensions) { if ($dimensions === []) { $this->outputLine('Workspace "' . $workspaceName . '" without dimensions done. (Indexed ' . $indexedNodes . ' nodes)'); } else { $this->outputLine('Workspace "' . $workspaceName . '" and dimensions "' . json_encode($dimensions) . '" done. (Indexed ' . $indexedNodes . ' nodes)'); } }; if ($workspace === null) { foreach ($this->workspaceRepository->findAll() as $workspace) { $count += $this->indexWorkspace($workspace->getName(), $limit, $callback); } } else { $count += $this->indexWorkspace($workspace, $limit, $callback); } $this->nodeIndexingManager->flushQueues(); $this->logger->log('Done. (indexed ' . $count . ' nodes)', LOG_INFO); $this->nodeIndexer->getIndex()->refresh(); // TODO: smoke tests if ($update === false) { $this->nodeIndexer->updateIndexAlias(); } }
/** * Display a list of unpublished content * * @param Workspace $workspace * @return void * @todo Pagination * @todo Tree filtering + level limit * @todo Search field * @todo Difference mechanism */ public function indexAction(Workspace $workspace = NULL) { if ($workspace === NULL) { $workspace = $this->userService->getUserWorkspace(); } $sites = array(); foreach ($this->publishingService->getUnpublishedNodes($workspace) as $node) { $pathParts = explode('/', $node->getPath()); if (count($pathParts) > 2) { $siteNodeName = $pathParts[2]; $q = new FlowQuery(array($node)); $document = $q->closest('[instanceof TYPO3.Neos:Document]')->get(0); // FIXME: $document will be NULL if we have a broken rootline for this node. This actually should never happen, but currently can in some scenarios. if ($document !== NULL) { $documentPath = implode('/', array_slice(explode('/', $document->getPath()), 3)); $relativePath = str_replace(sprintf('/sites/%s/%s', $siteNodeName, $documentPath), '', $node->getPath()); if (!isset($sites[$siteNodeName]['siteNode'])) { $sites[$siteNodeName]['siteNode'] = $this->siteRepository->findOneByNodeName($siteNodeName); } $sites[$siteNodeName]['documents'][$documentPath]['documentNode'] = $document; $change = array('node' => $node); if ($node->getNodeType()->isOfType('TYPO3.Neos:Node')) { $change['configuration'] = $node->getNodeType()->getFullConfiguration(); } $sites[$siteNodeName]['documents'][$documentPath]['changes'][$relativePath] = $change; } } } $liveContext = $this->contextFactory->create(array('workspaceName' => 'live')); ksort($sites); foreach ($sites as $siteKey => $site) { foreach ($site['documents'] as $documentKey => $document) { foreach ($document['changes'] as $changeKey => $change) { $liveNode = $liveContext->getNodeByIdentifier($change['node']->getIdentifier()); $sites[$siteKey]['documents'][$documentKey]['changes'][$changeKey]['isNew'] = is_null($liveNode); $sites[$siteKey]['documents'][$documentKey]['changes'][$changeKey]['isMoved'] = $liveNode && $change['node']->getPath() !== $liveNode->getPath(); } } ksort($sites[$siteKey]['documents']); } $workspaces = array(); foreach ($this->workspaceRepository->findAll() as $workspaceInstance) { array_push($workspaces, array('workspaceNode' => $workspaceInstance, 'unpublishedNodesCount' => $this->publishingService->getUnpublishedNodesCount($workspaceInstance))); } $this->view->assignMultiple(array('workspace' => $workspace, 'workspaces' => $workspaces, 'sites' => $sites)); }
/** * Update a site * * @param Site $site A site to update * @param string $newSiteNodeName A new site node name * @return void * @Flow\Validate(argumentName="$site", type="UniqueEntity") * @Flow\Validate(argumentName="$newSiteNodeName", type="NotEmpty") * @Flow\Validate(argumentName="$newSiteNodeName", type="StringLength", options={ "minimum"=1, "maximum"=250 }) * @Flow\Validate(argumentName="$newSiteNodeName", type="TYPO3.Neos:NodeName") */ public function updateSiteAction(Site $site, $newSiteNodeName) { if ($site->getNodeName() !== $newSiteNodeName) { $oldSiteNodePath = NodePaths::addNodePathSegment(SiteService::SITES_ROOT_PATH, $site->getNodeName()); $newSiteNodePath = NodePaths::addNodePathSegment(SiteService::SITES_ROOT_PATH, $newSiteNodeName); /** @var $workspace Workspace */ foreach ($this->workspaceRepository->findAll() as $workspace) { $siteNode = $this->nodeDataRepository->findOneByPath($oldSiteNodePath, $workspace); if ($siteNode !== null) { $siteNode->setPath($newSiteNodePath); } } $site->setNodeName($newSiteNodeName); $this->nodeDataRepository->persistEntities(); } $this->siteRepository->update($site); $this->addFlashMessage('The site "%s" has been updated.', 'Update', null, array(htmlspecialchars($site->getName())), 1412371798); $this->unsetLastVisitedNodeAndRedirect('index'); }
/** * Update a site * * @param Site $site A site to update * @param string $newSiteNodeName A new site node name * @return void * @Flow\Validate(argumentName="$site", type="UniqueEntity") * @Flow\Validate(argumentName="$newSiteNodeName", type="NotEmpty") * @Flow\Validate(argumentName="$newSiteNodeName", type="StringLength", options={ "minimum"=1, "maximum"=250 }) * @Flow\Validate(argumentName="$newSiteNodeName", type="TYPO3.Neos:NodeName") */ public function updateSiteAction(Site $site, $newSiteNodeName) { if ($site->getNodeName() !== $newSiteNodeName) { $oldSiteNodePath = '/sites/' . $site->getNodeName(); $newSiteNodePath = '/sites/' . $newSiteNodeName; /** @var $workspace Workspace */ foreach ($this->workspaceRepository->findAll() as $workspace) { $siteNode = $this->nodeDataRepository->findOneByPath($oldSiteNodePath, $workspace); if ($siteNode !== NULL) { $siteNode->setPath($newSiteNodePath); } } $site->setNodeName($newSiteNodeName); $this->nodeDataRepository->persistEntities(); } $this->siteRepository->update($site); $this->addFlashMessage('The site "%s" has been updated.', 'Update', NULL, array($site->getName()), 1412371798); $this->unsetLastVisitedNodeAndRedirect('index'); }
/** * Index all nodes by creating a new index and when everything was completed, switch the index alias. * * This command (re-)indexes all nodes contained in the content repository and sets the schema beforehand. * * @param integer $limit Amount of nodes to index at maximum * @param boolean $update if TRUE, do not throw away the index at the start. Should *only be used for development*. * @param string $workspace name of the workspace which should be indexed * @return void */ public function buildCommand($limit = NULL, $update = FALSE, $workspace = NULL) { if ($update === TRUE) { $this->logger->log('!!! Update Mode (Development) active!', LOG_INFO); } else { $this->nodeIndexer->setIndexNamePostfix(time()); $this->nodeIndexer->getIndex()->create(); $this->logger->log('Created index ' . $this->nodeIndexer->getIndexName(), LOG_INFO); $nodeTypeMappingCollection = $this->nodeTypeMappingBuilder->buildMappingInformation($this->nodeIndexer->getIndex()); foreach ($nodeTypeMappingCollection as $mapping) { /** @var \Flowpack\ElasticSearch\Domain\Model\Mapping $mapping */ $mapping->apply(); } $this->logger->log('Updated Mapping.', LOG_INFO); } $this->logger->log(sprintf('Indexing %snodes ... ', $limit !== NULL ? 'the first ' . $limit . ' ' : ''), LOG_INFO); $count = 0; $this->limit = $limit; $this->indexedNodes = 0; $this->countedIndexedNodes = 0; if ($workspace === NULL && $this->settings['indexAllWorkspaces'] === FALSE) { $workspace = 'live'; } if ($workspace === NULL) { foreach ($this->workspaceRepository->findAll() as $workspace) { $this->indexWorkspace($workspace->getName()); $count = $count + $this->countedIndexedNodes; } } else { $this->indexWorkspace($workspace); $count = $count + $this->countedIndexedNodes; } $this->nodeIndexingManager->flushQueues(); $this->logger->log('Done. (indexed ' . $count . ' nodes)', LOG_INFO); $this->nodeIndexer->getIndex()->refresh(); // TODO: smoke tests if ($update === FALSE) { $this->nodeIndexer->updateIndexAlias(); } }
/** * @return \TYPO3\FLOW3\Persistence\QueryResultInterface */ public function getWorkspaces() { return $this->workspaceRepository->findAll(); }
/** * Shows a list of existing workspaces * * @return string */ public function indexAction() { $this->view->assign('workspaces', $this->workspaceRepository->findAll()); }