/** * Executes this task * * @param \TYPO3\Deploy\Domain\Model\Node $node * @param \TYPO3\Deploy\Domain\Model\Application $application * @param \TYPO3\Deploy\Domain\Model\Deployment $deployment * @param array $options * @return void */ public function execute(Node $node, Application $application, Deployment $deployment, array $options = array()) { $targetReleasePath = $deployment->getApplicationReleasePath($application); $username = $node->getOption('username'); $hostname = $node->getHostname(); $configurationPath = $this->getDeploymentConfigurationPath() . '/Configuration/' . $deployment->getName() . '/'; $encryptedConfiguration = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($configurationPath, 'yaml.encrypted'); if (count($encryptedConfiguration) > 0) { throw new \Exception('You have sealed configuration files, please open the configuration for "' . $deployment->getName() . '"', 1317229449); } $configurations = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($configurationPath, 'yaml'); $commands = array(); foreach ($configurations as $configuration) { $targetConfigurationPath = dirname(str_replace($configurationPath, '', $configuration)); $commands[] = "scp {$configuration} {$username}@{$hostname}:{$targetReleasePath}/Configuration/{$targetConfigurationPath}/"; } $localhost = new Node('localhost'); $localhost->setHostname('localhost'); $this->shell->executeOrSimulate($commands, $localhost, $deployment); }
/** * Recursively publishes static resources located in the specified directory. * These resources are typically public package resources provided by the active packages. * * @param string $sourcePath The full path to the source directory which should be published (includes sub directories) * @param string $relativeTargetPath Path relative to the target's root where resources should be published to. * @return boolean TRUE if publication succeeded or FALSE if the resources could not be published */ public function publishStaticResources($sourcePath, $relativeTargetPath) { if (!is_dir($sourcePath)) { return FALSE; } $sourcePath = rtrim(\TYPO3\FLOW3\Utility\Files::getUnixStylePath($this->realpath($sourcePath)), '/'); $targetPath = rtrim(\TYPO3\FLOW3\Utility\Files::concatenatePaths(array($this->resourcesPublishingPath, 'Static', $relativeTargetPath)), '/'); if ($this->settings['resource']['publishing']['fileSystem']['mirrorMode'] === 'link') { if (\TYPO3\FLOW3\Utility\Files::is_link($targetPath) && rtrim(\TYPO3\FLOW3\Utility\Files::getUnixStylePath($this->realpath($targetPath)), '/') === $sourcePath) { return TRUE; } elseif (is_dir($targetPath)) { \TYPO3\FLOW3\Utility\Files::removeDirectoryRecursively($targetPath); } elseif (is_link($targetPath)) { unlink($targetPath); } else { \TYPO3\FLOW3\Utility\Files::createDirectoryRecursively(dirname($targetPath)); } symlink($sourcePath, $targetPath); } else { foreach (\TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($sourcePath) as $sourcePathAndFilename) { if (substr(strtolower($sourcePathAndFilename), -4, 4) === '.php') { continue; } $targetPathAndFilename = \TYPO3\FLOW3\Utility\Files::concatenatePaths(array($targetPath, str_replace($sourcePath, '', $sourcePathAndFilename))); if (!file_exists($targetPathAndFilename) || filemtime($sourcePathAndFilename) > filemtime($targetPathAndFilename)) { $this->mirrorFile($sourcePathAndFilename, $targetPathAndFilename, TRUE); } } } return TRUE; }
/** * Look for code migration files in the given package path and register them * for further action. * * @param string $packagePath * @return void */ protected function registerMigrationFiles($packagePath) { $packagePath = rtrim($packagePath, '/'); $packageKey = substr($packagePath, strrpos($packagePath, '/') + 1); $migrationsDirectory = Files::concatenatePaths(array($packagePath, 'Migrations/Code')); if (!is_dir($migrationsDirectory)) { return; } $migrationFilenames = Files::readDirectoryRecursively($migrationsDirectory, '.php'); foreach ($migrationFilenames as $filenameAndPath) { require_once $filenameAndPath; $baseFilename = basename($filenameAndPath, '.php'); $version = substr($baseFilename, 7); $classname = 'TYPO3\\FLOW3\\Core\\Migrations\\' . $baseFilename; $this->migrations[$version] = new $classname($this, $packageKey); } ksort($this->migrations); }
/** * Destroys (file) data from all active PHP sessions. * * @param \TYPO3\FLOW3\Core\Bootstrap $bootstrap * @return integer The number of session files which have been removed */ public static function destroyAll(Bootstrap $bootstrap) { $settings = $bootstrap->getObjectManager()->get('TYPO3\\FLOW3\\Configuration\\ConfigurationManager')->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'TYPO3.FLOW3'); if (empty($settings['session']['PhpSession']['savePath'])) { $sessionsPath = Files::concatenatePaths(array($bootstrap->getObjectManager()->get('TYPO3\\FLOW3\\Utility\\Environment')->getPathToTemporaryDirectory(), 'Sessions')); } else { $sessionsPath = $settings['session']['PhpSession']['savePath']; } if (is_dir($sessionsPath)) { $filenames = Files::readDirectoryRecursively($sessionsPath); if (count($filenames) > 0) { Files::emptyDirectoryRecursively($sessionsPath); } return count($filenames); } else { return 0; } }
/** * Validate the given configuration * * ./flow3 configuration:validate --type Settings --path TYPO3.FLOW3.persistence * * The schemas are searched in the path "Resources/Private/Schema" of all * active Packages. The schema-filenames must match the pattern * __type__.__path__.schema.yaml. The type and/or the path can also be * expressed as subdirectories of Resources/Private/Schema. So * Settings/TYPO3/FLOW3.persistence.schema.yaml will match the same pathes * like Settings.TYPO3.FLOW3.persistence.schema.yaml or * Settings/TYPO3.FLOW3/persistence.schema.yaml * * @param string $type Configuration type to validate * @param string $path path to the subconfiguration separated by "." like "TYPO3.FLOW3" * @return void */ public function validateCommand($type = NULL, $path = NULL) { $availableConfigurationTypes = $this->configurationManager->getAvailableConfigurationTypes(); if (in_array($type, $availableConfigurationTypes) === FALSE) { if ($type !== NULL) { $this->outputLine('<b>Configuration type "%s" was not found!</b>', array($type)); $this->outputLine(); } $this->outputLine('<b>Available configuration types:</b>'); foreach ($availableConfigurationTypes as $availableConfigurationType) { $this->outputLine(' ' . $availableConfigurationType); } $this->outputLine(); $this->outputLine('Hint: <b>%s configuration:validate --type <configurationType></b>', array($this->getFlow3InvocationString())); $this->outputLine(' validates the configuration of the specified type.'); return; } $configuration = $this->configurationManager->getConfiguration($type); $this->outputLine('<b>Validating configuration for type: "' . $type . '"' . ($path !== NULL ? ' and path: "' . $path . '"' : '') . '</b>'); // find schema files for the given type and path $schemaFileInfos = array(); $activePackages = $this->packageManager->getActivePackages(); foreach ($activePackages as $package) { $packageKey = $package->getPackageKey(); $packageSchemaPath = \TYPO3\FLOW3\Utility\Files::concatenatePaths(array($package->getResourcesPath(), 'Private/Schema')); if (is_dir($packageSchemaPath)) { $packageSchemaFiles = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($packageSchemaPath, '.schema.yaml'); foreach ($packageSchemaFiles as $schemaFile) { $schemaName = substr($schemaFile, strlen($packageSchemaPath) + 1, -strlen('.schema.yaml')); $schemaNameParts = explode('.', str_replace('/', '.', $schemaName), 2); $schemaType = $schemaNameParts[0]; $schemaPath = isset($schemaNameParts[1]) ? $schemaNameParts[1] : NULL; if ($schemaType === $type && ($path === NULL || strpos($schemaPath, $path) === 0)) { $schemaFileInfos[] = array('file' => $schemaFile, 'name' => $schemaName, 'path' => $schemaPath, 'packageKey' => $packageKey); } } } } $this->outputLine(); if (count($schemaFileInfos) > 0) { $this->outputLine('%s schema files were found:', array(count($schemaFileInfos))); $result = new \TYPO3\FLOW3\Error\Result(); foreach ($schemaFileInfos as $schemaFileInfo) { if ($schemaFileInfo['path'] !== NULL) { $data = \TYPO3\FLOW3\Utility\Arrays::getValueByPath($configuration, $schemaFileInfo['path']); } else { $data = $configuration; } if (empty($data)) { $result->forProperty($schemaFileInfo['path'])->addError(new \TYPO3\FLOW3\Error\Error('configuration in path ' . $schemaFileInfo['path'] . ' is empty')); $this->outputLine(' - package: "' . $schemaFileInfo['packageKey'] . '" schema: "' . $schemaFileInfo['name'] . '" -> <b>configuration is empty</b>'); } else { $parsedSchema = \Symfony\Component\Yaml\Yaml::parse($schemaFileInfo['file']); $schemaResult = $this->schemaValidator->validate($data, $parsedSchema); if ($schemaResult->hasErrors()) { $this->outputLine(' - package:"' . $schemaFileInfo['packageKey'] . '" schema:"' . $schemaFileInfo['name'] . '" -> <b>' . count($schemaResult->getFlattenedErrors()) . ' errors</b>'); } else { $this->outputLine(' - package:"' . $schemaFileInfo['packageKey'] . '" schema:"' . $schemaFileInfo['name'] . '" -> <b>is valid</b>'); } if ($schemaFileInfo['path'] !== NULL) { $result->forProperty($schemaFileInfo['path'])->merge($schemaResult); } else { $result->merge($schemaResult); } } } } else { $this->outputLine('No matching schema-files were found!'); return; } $this->outputLine(); if ($result->hasErrors()) { $errors = $result->getFlattenedErrors(); $this->outputLine('<b>%s errors were found:</b>', array(count($errors))); foreach ($errors as $path => $pathErrors) { foreach ($pathErrors as $error) { $this->outputLine(' - %s -> %s', array($path, $error->render())); } } } else { $this->outputLine('<b>The configuration is valid!</b>'); } }
/** * Applies all registered searchAndReplace and searchAndReplaceRegex operations. * * @return void */ protected function applySearchAndReplaceOperations() { $allPathsAndFilenames = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($this->packageData['path'], NULL, TRUE); foreach ($this->operations['searchAndReplace'] as $operation) { foreach ($allPathsAndFilenames as $pathAndFilename) { $pathInfo = pathinfo($pathAndFilename); if (!isset($pathInfo['filename'])) { continue; } if (strpos($pathAndFilename, 'Migrations/Code') !== FALSE) { continue; } if ($operation[2] !== array()) { if (!isset($pathInfo['extension']) || !in_array($pathInfo['extension'], $operation[2], TRUE)) { continue; } } Tools::searchAndReplace($operation[0], $operation[1], $pathAndFilename); } } }
/** * Open encrypted configuration with the local key * * Like the seal command, this can be restricted to a specific deployment. If a passphrase * was used to encrypt the local private key, it must be specified as the passphrase * argument to open the configuration files. * * @param string $passphrase Passphrase to decrypt the local key (if encrypted) * @param string $deploymentName Optional deployment name to selectively decrypt the configuration * @return void * @see typo3.deploy:encrypt:seal * @author Christopher Hlubek <*****@*****.**> */ public function openCommand($passphrase = NULL, $deploymentName = '') { $keyPair = $this->readKeyPair($this->getDeploymentConfigurationPath() . '/Keys/Local.key'); try { $keyPair = $this->encryptionService->openKeyPair($keyPair, $passphrase); } catch (\TYPO3\Deploy\Encryption\InvalidPassphraseException $exception) { $this->outputLine('Local key is encrypted with passphrase. Wrong or no passphrase given.'); $this->quit(1); } $configurations = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($this->getDeploymentConfigurationPath() . '/Configuration/' . $deploymentName, 'yaml.encrypted'); foreach ($configurations as $configuration) { $crypted = file_get_contents($configuration); $data = $this->encryptionService->decryptData($crypted, $keyPair->getPrivateKey()); $targetFilename = substr($configuration, 0, -strlen('.encrypted')); file_put_contents($targetFilename, $data); unlink($configuration); $this->outputLine('Opened ' . $targetFilename); } }
/** * Detects changes of the files and directories to be monitored and emits signals * accordingly. * * @return void * @api */ public function detectChanges() { $changedDirectories = array(); $changedFiles = $this->detectChangedFiles($this->monitoredFiles); foreach ($this->monitoredDirectories as $path) { if (!isset($this->directoriesAndFiles[$path])) { $this->directoriesAndFiles[$path] = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($path); $this->directoriesChanged = TRUE; $changedDirectories[$path] = \TYPO3\FLOW3\Monitor\ChangeDetectionStrategy\ChangeDetectionStrategyInterface::STATUS_CREATED; } } foreach ($this->directoriesAndFiles as $path => $pathAndFilenames) { if (!is_dir($path)) { unset($this->directoriesAndFiles[$path]); $this->directoriesChanged = TRUE; $changedDirectories[$path] = \TYPO3\FLOW3\Monitor\ChangeDetectionStrategy\ChangeDetectionStrategyInterface::STATUS_DELETED; } else { $currentSubDirectoriesAndFiles = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($path); if ($currentSubDirectoriesAndFiles != $pathAndFilenames) { $pathAndFilenames = array_unique(array_merge($currentSubDirectoriesAndFiles, $pathAndFilenames)); } $changedFiles = array_merge($changedFiles, $this->detectChangedFiles($pathAndFilenames)); } } if (count($changedFiles) > 0) { $this->emitFilesHaveChanged($this->identifier, $changedFiles); } if (count($changedDirectories) > 0) { $this->emitDirectoriesHaveChanged($this->identifier, $changedDirectories); } if (count($changedFiles) > 0 || count($changedDirectories) > 0) { $this->systemLogger->log(sprintf('File Monitor "%s" detected %s changed files and %s changed directories.', $this->identifier, count($changedFiles), count($changedDirectories)), LOG_INFO); } }
/** * Imports the specified bundle into the configured "importRootNodePath". * * @param string $bundle * @return void */ protected function importBundle($bundle) { $this->outputLine('Importing bundle "%s"', array($bundle)); $renderedDocumentationRootPath = rtrim($this->bundleConfiguration['renderedDocumentationRootPath'], '/'); $contentContext = new \TYPO3\TYPO3\Domain\Service\ContentContext('live'); $contentContext->setInvisibleContentShown(TRUE); $siteNode = $contentContext->getCurrentSiteNode(); $importRootNode = $siteNode->getNode($this->bundleConfiguration['importRootNodePath']); if ($importRootNode === NULL) { $this->output('ImportRootNode "%s" does not exist!', array($this->bundleConfiguration['importRootNodePath'])); $this->quit(1); } if (!is_dir($renderedDocumentationRootPath)) { $this->outputLine('The folder "%s" does not exist. Did you render the documentation?', array($renderedDocumentationRootPath)); $this->quit(1); } $unorderedJsonFileNames = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($renderedDocumentationRootPath, '.fjson'); if ($unorderedJsonFileNames === array()) { $this->outputLine('The folder "%s" contains no fjson files. Did you render the documentation?', array($renderedDocumentationRootPath)); $this->quit(1); } $orderedNodePaths = array(); foreach ($unorderedJsonFileNames as $jsonPathAndFileName) { if (basename($jsonPathAndFileName) === 'Index.fjson') { $chapterRelativeNodePath = substr($jsonPathAndFileName, strlen($renderedDocumentationRootPath), -12) . '/'; # $orderedNodePaths[] = $this->normalizeNodePath(substr($chapterRelativeNodePath, 0, -1)); $indexArray = json_decode(file_get_contents($jsonPathAndFileName), TRUE); foreach (explode(chr(10), $indexArray['body']) as $tocHtmlLine) { preg_match('!^\\<li class="toctree-l1"\\>\\<a class="reference internal" href="\\.\\./([a-zA-Z0-9]+)/.*$!', $tocHtmlLine, $matches); if ($matches !== array()) { $orderedNodePaths[] = $this->normalizeNodePath($chapterRelativeNodePath . $matches[1]); } } } } foreach ($unorderedJsonFileNames as $jsonPathAndFileName) { $data = json_decode(file_get_contents($jsonPathAndFileName)); if (!isset($data->body)) { continue; } $relativeNodePath = substr($jsonPathAndFileName, strlen($renderedDocumentationRootPath) + 1, -6); $relativeNodePath = $this->normalizeNodePath($relativeNodePath); $segments = explode('/', $relativeNodePath); $pageNode = $importRootNode; while ($segment = array_shift($segments)) { $nodeName = preg_replace('/[^a-z0-9\\-]/', '', $segment); $subPageNode = $pageNode->getNode($nodeName); if ($subPageNode === NULL) { $this->outputLine('Creating page node "%s"', array($relativeNodePath)); $subPageNode = $pageNode->createNode($nodeName, 'TYPO3.TYPO3:Page'); if (!$subPageNode->hasProperty('title')) { $subPageNode->setProperty('title', $nodeName); } } $pageNode = $subPageNode; } $sectionNode = $pageNode->getNode('main'); if ($sectionNode === NULL) { $this->outputLine('Creating section node "%s"', array($relativeNodePath . '/main')); $sectionNode = $pageNode->createNode('main', 'TYPO3.TYPO3:Section'); } $textNode = $sectionNode->getNode('text1'); if ($textNode === NULL) { $this->outputLine('Creating text node "%s"', array($relativeNodePath . '/main/text1')); $textNode = $sectionNode->createNode('text1', 'TYPO3.TYPO3:Text'); } $pageNode->setProperty('title', $data->title); $this->outputLine('Setting page title of page "%s" to "%s"', array($relativeNodePath, $data->title)); $bodyText = $this->prepareBodyText($data->body, $relativeNodePath); $textNode->setProperty('text', $bodyText); } $importRootNodePath = $importRootNode->getPath(); $currentParentNodePath = ''; foreach ($orderedNodePaths as $nodePath) { $node = $importRootNode->getNode($importRootNodePath . $nodePath); if ($node !== NULL) { if ($node->getParent()->getPath() !== $currentParentNodePath) { $currentParentNodePath = $node->getParent()->getPath(); $previousNode = NULL; } if ($previousNode !== NULL) { $this->outputLine('Moved node %s', array($node->getPath())); $this->outputLine('after node %s', array($previousNode->getPath())); $node->moveAfter($previousNode); } else { // FIXME: Node->isFirst() or Node->moveFirst() would be needed here } $previousNode = $node; } else { $this->outputLine('Node %s does not exist.', array($importRootNodePath . $nodePath)); } } $this->siteRepository->update($contentContext->getCurrentSite()); }