/** * Fetches the site with the given name and exports it into XML in the given package. * * @param array<Site> $sites * @param boolean $tidy Whether to export formatted XML * @param string $packageKey Package key where the export output should be saved to * @param string $nodeTypeFilter Filter the node type of the nodes, allows complex expressions (e.g. "TYPO3.Neos:Page", "!TYPO3.Neos:Page,TYPO3.Neos:Text") * @return void * @throws NeosException */ public function exportToPackage(array $sites, $tidy = false, $packageKey, $nodeTypeFilter = null) { if (!$this->packageManager->isPackageActive($packageKey)) { throw new NeosException(sprintf('Error: Package "%s" is not active.', $packageKey), 1404375719); } $contentPathAndFilename = sprintf('resource://%s/Private/Content/Sites.xml', $packageKey); $this->resourcesPath = Files::concatenatePaths(array(dirname($contentPathAndFilename), 'Resources')); Files::createDirectoryRecursively($this->resourcesPath); $this->xmlWriter = new \XMLWriter(); $this->xmlWriter->openUri($contentPathAndFilename); $this->xmlWriter->setIndent($tidy); $this->exportSites($sites, $nodeTypeFilter); $this->xmlWriter->flush(); }
/** * @return void */ public function indexAction() { $packageGroups = array(); foreach ($this->packageManager->getAvailablePackages() as $package) { /** @var \TYPO3\Flow\Package $package */ $packagePath = substr($package->getPackagepath(), strlen(FLOW_PATH_PACKAGES)); $packageGroup = substr($packagePath, 0, strpos($packagePath, '/')); $packageGroups[$packageGroup][$package->getPackageKey()] = array('sanitizedPackageKey' => str_replace('.', '', $package->getPackageKey()), 'version' => $package->getPackageMetaData()->getVersion(), 'name' => $package->getComposerManifest('name'), 'type' => $package->getComposerManifest('type'), 'description' => $package->getPackageMetaData()->getDescription(), 'metaData' => $package->getPackageMetaData(), 'isActive' => $this->packageManager->isPackageActive($package->getPackageKey()), 'isFrozen' => $this->packageManager->isPackageFrozen($package->getPackageKey()), 'isProtected' => $package->isProtected()); } ksort($packageGroups); foreach (array_keys($packageGroups) as $packageGroup) { ksort($packageGroups[$packageGroup]); } $this->view->assignMultiple(array('packageGroups' => $packageGroups, 'isDevelopmentContext' => $this->objectManager->getContext()->isDevelopment())); }
/** * Index action * * @return void */ public function indexAction() { $this->view->assign('flowPathRoot', realpath(FLOW_PATH_ROOT)); $this->view->assign('flowPathWeb', realpath(FLOW_PATH_WEB)); $this->view->assign('isMyPackageActive', $this->packageManager->isPackageActive('MyCompany.MyPackage')); $baseUri = $this->request->getHttpRequest()->getBaseUri(); $this->view->assign('baseUri', $baseUri); $this->view->assign('isWindows', DIRECTORY_SEPARATOR !== '/'); $flowPackage = $this->packageManager->getPackage('TYPO3.Flow'); $version = $flowPackage->getPackageMetaData()->getVersion(); $this->view->assign('version', $version); $activePackages = $this->packageManager->getActivePackages(); $this->view->assign('activePackages', $activePackages); $this->view->assign('notDevelopmentContext', !$this->objectManager->getContext()->isDevelopment()); }
/** * Create a new site. * * @param string $site Site to import * @param string $packageKey Package Name to create * @param string $siteName Site Name to create * @Flow\Validate(argumentName="$packageKey", type="\TYPO3\Neos\Validation\Validator\PackageKeyValidator") * @return void */ public function createSiteAction($site, $packageKey = '', $siteName = '') { if ($packageKey !== '' && $this->packageManager->isPackageActive('TYPO3.Neos.Kickstarter')) { if ($this->packageManager->isPackageAvailable($packageKey)) { $this->addFlashMessage('The package key "%s" already exists.', 'Invalid package key', Message::SEVERITY_ERROR, array(htmlspecialchars($packageKey)), 1412372021); $this->redirect('index'); } $generatorService = $this->objectManager->get('TYPO3\\Neos\\Kickstarter\\Service\\GeneratorService'); $generatorService->generateSitePackage($packageKey, $siteName); } else { $packageKey = $site; } $deactivatedSitePackages = $this->deactivateAllOtherSitePackages($packageKey); if (count($deactivatedSitePackages) > 0) { $this->flashMessageContainer->addMessage(new Message(sprintf('The existing Site Packages "%s" were deactivated, in order to prevent interactions with the newly created package "%s".', htmlspecialchars(implode(', ', $deactivatedSitePackages)), htmlspecialchars($packageKey)))); } $this->packageManager->activatePackage($packageKey); if ($packageKey !== '') { try { $this->siteImportService->importFromPackage($packageKey); $this->addFlashMessage('The site has been created.', '', null, array(), 1412372266); } catch (\Exception $exception) { $this->systemLogger->logException($exception); $this->addFlashMessage('Error: During the import of the "Sites.xml" from the package "%s" an exception occurred: %s', 'Import error', Message::SEVERITY_ERROR, array(htmlspecialchars($packageKey), htmlspecialchars($exception->getMessage())), 1412372375); } } else { $this->addFlashMessage('No site selected for import and no package name provided.', 'No site selected', Message::SEVERITY_ERROR, array(), 1412372554); $this->redirect('newSite'); } $this->unsetLastVisitedNodeAndRedirect('index'); }
/** * Returns the form definitions for the step * * @param \TYPO3\Form\Core\Model\FormDefinition $formDefinition * @return void */ protected function buildForm(\TYPO3\Form\Core\Model\FormDefinition $formDefinition) { $page1 = $formDefinition->createPage('page1'); $page1->setRenderingOption('header', 'Create a new site'); $introduction = $page1->createElement('introduction', 'TYPO3.Form:StaticText'); $introduction->setProperty('text', 'There are two ways of creating a site. Choose between the following:'); $importSection = $page1->createElement('import', 'TYPO3.Form:Section'); $importSection->setLabel('Import a site from an existing site package'); $sitePackages = array(); foreach ($this->packageManager->getFilteredPackages('available', NULL, 'typo3-flow-site') as $package) { $sitePackages[$package->getPackageKey()] = $package->getPackageKey(); } if (count($sitePackages) > 0) { $site = $importSection->createElement('site', 'TYPO3.Form:SingleSelectDropdown'); $site->setLabel('Select a site package'); $site->setProperty('options', $sitePackages); $site->addValidator(new \TYPO3\Flow\Validation\Validator\NotEmptyValidator()); $sites = $this->siteRepository->findAll(); if ($sites->count() > 0) { $prune = $importSection->createElement('prune', 'TYPO3.Form:Checkbox'); $prune->setLabel('Delete existing sites'); } } else { $error = $importSection->createElement('noSitePackagesError', 'TYPO3.Form:StaticText'); $error->setProperty('text', 'No site packages were available, make sure you have an active site package'); $error->setProperty('elementClassAttribute', 'alert alert-warning'); } if ($this->packageManager->isPackageActive('TYPO3.Neos.Kickstarter')) { $separator = $page1->createElement('separator', 'TYPO3.Form:StaticText'); $separator->setProperty('elementClassAttribute', 'section-separator'); $newPackageSection = $page1->createElement('newPackageSection', 'TYPO3.Form:Section'); $newPackageSection->setLabel('Create a new site package with a dummy site'); $packageName = $newPackageSection->createElement('packageKey', 'TYPO3.Form:SingleLineText'); $packageName->setLabel('Package Name (in form "Vendor.DomainCom")'); $packageName->addValidator(new \TYPO3\Neos\Validation\Validator\PackageKeyValidator()); $siteName = $newPackageSection->createElement('siteName', 'TYPO3.Form:SingleLineText'); $siteName->setLabel('Site Name (e.g. "domain.com")'); } else { $error = $importSection->createElement('neosKickstarterUnavailableError', 'TYPO3.Form:StaticText'); $error->setProperty('text', 'The Neos Kickstarter package (TYPO3.Neos.Kickstarter) is not installed, install it for kickstarting new sites (using "composer require typo3/neos-kickstarter")'); $error->setProperty('elementClassAttribute', 'alert alert-warning'); } $explanation = $page1->createElement('explanation', 'TYPO3.Form:StaticText'); $explanation->setProperty('text', 'Notice the difference between a site package and a site. A site package is a Flow package that can be used for creating multiple site instances.'); $explanation->setProperty('elementClassAttribute', 'alert alert-info'); $step = $this; $callback = function (\TYPO3\Form\Core\Model\FinisherContext $finisherContext) use($step) { $step->importSite($finisherContext); }; $this->closureFinisher = new \TYPO3\Form\Finishers\ClosureFinisher(); $this->closureFinisher->setOption('closure', $callback); $formDefinition->addFinisher($this->closureFinisher); $formDefinition->setRenderingOption('skipStepNotice', 'You can always import a site using the site:import command'); }
/** * Imports one or multiple sites from the XML file at $pathAndFilename * * @param string $pathAndFilename * @return Site The imported site * @throws UnknownPackageException|InvalidPackageStateException|NeosException */ public function importFromFile($pathAndFilename) { /** @var Site $importedSite */ $site = NULL; $this->eventEmittingService->withoutEventLog(function () use($pathAndFilename, &$site) { $xmlReader = new \XMLReader(); $xmlReader->open($pathAndFilename, NULL, LIBXML_PARSEHUGE); if ($this->workspaceRepository->findOneByName('live') === NULL) { $this->workspaceRepository->add(new Workspace('live')); } $rootNode = $this->contextFactory->create()->getRootNode(); $this->persistenceManager->persistAll(); $sitesNode = $rootNode->getNode('/sites'); if ($sitesNode === NULL) { $sitesNode = $rootNode->createSingleNode('sites'); } while ($xmlReader->read()) { if ($xmlReader->nodeType != \XMLReader::ELEMENT || $xmlReader->name !== 'site') { continue; } $isLegacyFormat = $xmlReader->getAttribute('nodeName') !== NULL && $xmlReader->getAttribute('state') === NULL && $xmlReader->getAttribute('siteResourcesPackageKey') === NULL; if ($isLegacyFormat) { $site = $this->legacySiteImportService->importSitesFromFile($pathAndFilename); return; } $site = $this->getSiteByNodeName($xmlReader->getAttribute('siteNodeName')); $site->setName($xmlReader->getAttribute('name')); $site->setState((int) $xmlReader->getAttribute('state')); $siteResourcesPackageKey = $xmlReader->getAttribute('siteResourcesPackageKey'); if (!$this->packageManager->isPackageAvailable($siteResourcesPackageKey)) { throw new UnknownPackageException(sprintf('Package "%s" specified in the XML as site resources package does not exist.', $siteResourcesPackageKey), 1303891443); } if (!$this->packageManager->isPackageActive($siteResourcesPackageKey)) { throw new InvalidPackageStateException(sprintf('Package "%s" specified in the XML as site resources package is not active.', $siteResourcesPackageKey), 1303898135); } $site->setSiteResourcesPackageKey($siteResourcesPackageKey); $rootNode = $this->contextFactory->create()->getRootNode(); // We fetch the workspace to be sure it's known to the persistence manager and persist all // so the workspace and site node are persisted before we import any nodes to it. $rootNode->getContext()->getWorkspace(); $this->persistenceManager->persistAll(); $sitesNode = $rootNode->getNode('/sites'); if ($sitesNode === NULL) { $sitesNode = $rootNode->createNode('sites'); } $this->nodeImportService->import($xmlReader, $sitesNode->getPath(), dirname($pathAndFilename) . '/Resources'); } }); if ($site === NULL) { throw new NeosException(sprintf('The XML file did not contain a valid site node.'), 1418999522); } $this->emitSiteImported($site); return $site; }
/** * @param string $packageKey * @return void */ public function deactivateAction($packageKey) { if ($this->packageManager->isPackageAvailable($packageKey) === false) { $this->flashMessageContainer->addMessage(new \TYPO3\Flow\Error\Error('Package ' . $packageKey . ' does not exists.')); $this->redirect('index'); } if ($this->packageManager->isPackageActive($packageKey) === false) { $this->flashMessageContainer->addMessage(new \TYPO3\Flow\Error\Error('Package ' . $packageKey . ' is currently not activated.')); $this->redirect('index'); } if ($requiredBy = $this->packageDependencyService->getPackageRequirements($packageKey)) { $this->flashMessageContainer->addMessage(new \TYPO3\Flow\Error\Error('Package ' . $packageKey . ' is required by ' . $requiredBy)); $this->redirect('index'); } $this->packageManager->deactivatePackage($packageKey); $this->redirect('index'); }
/** * Freeze a package * * This function marks a package as <b>frozen</b> in order to improve performance * in a development context. While a package is frozen, any modification of files * within that package won't be tracked and can lead to unexpected behavior. * * File monitoring won't consider the given package. Further more, reflection * data for classes contained in the package is cached persistently and loaded * directly on the first request after caches have been flushed. The precompiled * reflection data is stored in the <b>Configuration</b> directory of the * respective package. * * By specifying <b>all</b> as a package key, all currently frozen packages are * frozen (the default). * * @param string $packageKey Key of the package to freeze * @return void * @see typo3.flow:package:unfreeze * @see typo3.flow:package:refreeze */ public function freezeCommand($packageKey = 'all') { if (!$this->bootstrap->getContext()->isDevelopment()) { $this->outputLine('Package freezing is only supported in Development context.'); $this->quit(3); } $packagesToFreeze = []; if ($packageKey === 'all') { foreach (array_keys($this->packageManager->getActivePackages()) as $packageKey) { if (!$this->packageManager->isPackageFrozen($packageKey)) { $packagesToFreeze[] = $packageKey; } } if ($packagesToFreeze === []) { $this->outputLine('Nothing to do, all active packages were already frozen.'); $this->quit(0); } } elseif ($packageKey === 'blackberry') { $this->outputLine('http://bit.ly/freeze-blackberry'); $this->quit(42); } else { if (!$this->packageManager->isPackageActive($packageKey)) { if ($this->packageManager->isPackageAvailable($packageKey)) { $this->outputLine('Package "%s" is not active and thus cannot be frozen.', [$packageKey]); $this->quit(1); } else { $this->outputLine('Package "%s" is not available.', [$packageKey]); $this->quit(2); } } if ($this->packageManager->isPackageFrozen($packageKey)) { $this->outputLine('Package "%s" was already frozen.', [$packageKey]); $this->quit(0); } $packagesToFreeze = [$packageKey]; } foreach ($packagesToFreeze as $packageKey) { $this->packageManager->freezePackage($packageKey); $this->outputLine('Froze package "%s".', [$packageKey]); } }
/** * Imports one or multiple sites from the XML file at $pathAndFilename * * @param string $pathAndFilename * @return Site The imported site or NULL if not successful * @throws UnknownPackageException * @throws InvalidPackageStateException */ public function importSitesFromFile($pathAndFilename) { $contentContext = $this->createContext(); $sitesXmlString = $this->loadSitesXml($pathAndFilename); if (defined('LIBXML_PARSEHUGE')) { $options = LIBXML_PARSEHUGE; } else { $options = 0; } $site = null; $sitesXml = new \SimpleXMLElement($sitesXmlString, $options); foreach ($sitesXml->site as $siteXml) { $site = $this->getSiteByNodeName((string) $siteXml['nodeName']); if ((string) $siteXml->properties->name !== '') { $site->setName((string) $siteXml->properties->name); } $site->setState((int) $siteXml->properties->state); $siteResourcesPackageKey = (string) $siteXml->properties->siteResourcesPackageKey; if (!$this->packageManager->isPackageAvailable($siteResourcesPackageKey)) { throw new UnknownPackageException(sprintf('Package "%s" specified in the XML as site resources package does not exist.', $siteResourcesPackageKey), 1303891443); } if (!$this->packageManager->isPackageActive($siteResourcesPackageKey)) { throw new InvalidPackageStateException(sprintf('Package "%s" specified in the XML as site resources package is not active.', $siteResourcesPackageKey), 1303898135); } $site->setSiteResourcesPackageKey($siteResourcesPackageKey); $rootNode = $contentContext->getRootNode(); $sitesNode = $rootNode->getNode(SiteService::SITES_ROOT_PATH); if ($sitesNode === null) { $sitesNode = $rootNode->createSingleNode(NodePaths::getNodeNameFromPath(SiteService::SITES_ROOT_PATH)); } // We fetch the workspace to be sure it's known to the persistence manager and persist all // so the workspace and site node are persisted before we import any nodes to it. $rootNode->getContext()->getWorkspace(); $this->persistenceManager->persistAll(); if ($siteXml['type'] === null) { $this->upgradeLegacySiteXml($siteXml, $site); } $this->importNode($siteXml, $sitesNode); } return $site; }
/** * Clean up resource registry * * This command checks the resource registry (that is the database tables) for orphaned resource objects which don't * seem to have any corresponding data anymore (for example: the file in Data/Persistent/Resources has been deleted * without removing the related Resource object). * * If the TYPO3.Media package is active, this command will also detect any assets referring to broken resources * and will remove the respective Asset object from the database when the broken resource is removed. * * This command will ask you interactively what to do before deleting anything. * * @return void */ public function cleanCommand() { $this->outputLine('Checking if resource data exists for all known resource objects ...'); $this->outputLine(); $mediaPackagePresent = $this->packageManager->isPackageActive('TYPO3.Media'); $resourcesCount = $this->resourceRepository->countAll(); $this->output->progressStart($resourcesCount); $brokenResources = array(); $relatedAssets = new \SplObjectStorage(); foreach ($this->resourceRepository->findAll() as $resource) { $this->output->progressAdvance(1); /* @var \TYPO3\Flow\Resource\Resource $resource */ $stream = $resource->getStream(); if (!is_resource($stream)) { $brokenResources[] = $resource; } } $this->output->progressFinish(); $this->outputLine(); if ($mediaPackagePresent && count($brokenResources) > 0) { $assetRepository = $this->objectManager->get('TYPO3\\Media\\Domain\\Repository\\AssetRepository'); /* @var \TYPO3\Media\Domain\Repository\AssetRepository $assetRepository */ foreach ($brokenResources as $resource) { $assets = $assetRepository->findByResource($resource); if ($assets !== NULL) { $relatedAssets[$resource] = $assets; } } } if (count($brokenResources) > 0) { $this->outputLine('<b>Found %s broken resource(s):</b>', array(count($brokenResources))); $this->outputLine(); foreach ($brokenResources as $resource) { $this->outputLine('%s (%s) %s', array($resource->getFilename(), $resource->getSha1(), $resource->getCollectionName())); if (isset($relatedAssets[$resource])) { foreach ($relatedAssets[$resource] as $asset) { $this->outputLine(' -> %s (%s)', array(get_class($asset), $asset->getIdentifier())); } } } $response = NULL; while (!in_array($response, array('y', 'n', 'c'))) { $response = $this->output->ask('<comment>Do you want to remove all broken resource objects and related assets from the database? (y/n/c) </comment>'); } switch ($response) { case 'y': foreach ($brokenResources as $resource) { $resource->disableLifecycleEvents(); $this->persistenceManager->remove($resource); if (isset($relatedAssets[$resource])) { foreach ($relatedAssets[$resource] as $asset) { $assetRepository->remove($asset); } } } $this->outputLine('Removed %s resource object(s) from the database.', array(count($brokenResources))); break; case 'n': $this->outputLine('Did not delete any resource objects.'); break; case 'c': $this->outputLine('Stopping. Did not delete any resource objects.'); $this->quit(0); break; } } }
/** * Clean up resource registry * * This command checks the resource registry (that is the database tables) for orphaned resource objects which don't * seem to have any corresponding data anymore (for example: the file in Data/Persistent/Resources has been deleted * without removing the related Resource object). * * If the TYPO3.Media package is active, this command will also detect any assets referring to broken resources * and will remove the respective Asset object from the database when the broken resource is removed. * * This command will ask you interactively what to do before deleting anything. * * @return void */ public function cleanCommand() { $this->outputLine('Checking if resource data exists for all known resource objects ...'); $this->outputLine(); $mediaPackagePresent = $this->packageManager->isPackageActive('TYPO3.Media'); $resourcesCount = $this->resourceRepository->countAll(); $this->output->progressStart($resourcesCount); $brokenResources = []; $relatedAssets = new \SplObjectStorage(); $relatedThumbnails = new \SplObjectStorage(); $iterator = $this->resourceRepository->findAllIterator(); foreach ($this->resourceRepository->iterate($iterator, function ($iteration) { $this->clearState($iteration); }) as $resource) { $this->output->progressAdvance(1); /* @var Resource $resource */ $stream = $resource->getStream(); if (!is_resource($stream)) { $brokenResources[] = $resource->getSha1(); } } $this->output->progressFinish(); $this->outputLine(); if ($mediaPackagePresent && count($brokenResources) > 0) { /* @var AssetRepository $assetRepository */ $assetRepository = $this->objectManager->get(AssetRepository::class); /* @var ThumbnailRepository $thumbnailRepository */ $thumbnailRepository = $this->objectManager->get(ThumbnailRepository::class); foreach ($brokenResources as $key => $resourceSha1) { $resource = $this->resourceRepository->findOneBySha1($resourceSha1); $brokenResources[$key] = $resource; $assets = $assetRepository->findByResource($resource); if ($assets !== null) { $relatedAssets[$resource] = $assets; } $thumbnails = $thumbnailRepository->findByResource($resource); if ($assets !== null) { $relatedThumbnails[$resource] = $thumbnails; } } } if (count($brokenResources) > 0) { $this->outputLine('<b>Found %s broken resource(s):</b>', [count($brokenResources)]); $this->outputLine(); foreach ($brokenResources as $resource) { $this->outputLine('%s (%s) from "%s" collection', [$resource->getFilename(), $resource->getSha1(), $resource->getCollectionName()]); if (isset($relatedAssets[$resource])) { foreach ($relatedAssets[$resource] as $asset) { $this->outputLine(' -> %s (%s)', [get_class($asset), $asset->getIdentifier()]); } } } $response = null; while (!in_array($response, ['y', 'n', 'c'])) { $response = $this->output->ask('<comment>Do you want to remove all broken resource objects and related assets from the database? (y/n/c) </comment>'); } switch ($response) { case 'y': $brokenAssetCounter = 0; $brokenThumbnailCounter = 0; foreach ($brokenResources as $sha1 => $resource) { $this->outputLine('- delete %s (%s) from "%s" collection', [$resource->getFilename(), $resource->getSha1(), $resource->getCollectionName()]); $resource->disableLifecycleEvents(); $this->resourceRepository->remove($resource); if (isset($relatedAssets[$resource])) { foreach ($relatedAssets[$resource] as $asset) { $assetRepository->remove($asset); $brokenAssetCounter++; } } if (isset($relatedThumbnails[$resource])) { foreach ($relatedThumbnails[$resource] as $thumbnail) { $thumbnailRepository->remove($thumbnail); $brokenThumbnailCounter++; } } $this->persistenceManager->persistAll(); } $brokenResourcesCounter = count($brokenResources); if ($brokenResourcesCounter > 0) { $this->outputLine('Removed %s resource object(s) from the database.', [$brokenResourcesCounter]); } if ($brokenAssetCounter > 0) { $this->outputLine('Removed %s asset object(s) from the database.', [$brokenAssetCounter]); } if ($brokenThumbnailCounter > 0) { $this->outputLine('Removed %s thumbnail object(s) from the database.', [$brokenThumbnailCounter]); } break; case 'n': $this->outputLine('Did not delete any resource objects.'); break; case 'c': $this->outputLine('Stopping. Did not delete any resource objects.'); $this->quit(0); break; } } }
/** * Make sure the package is available and active * * @param string $packageKey * @throws \TYPO3\Flow\Error\Exception */ protected function assertPackageIsActive($packageKey) { if (!$this->packageManager->isPackageActive($packageKey)) { throw new \TYPO3\Flow\Error\Exception(sprintf('Error: Package "%s" is not active.', $packageKey), 1420643757); } }