/** * * @param array $tables * @param string $package * @return void * @author Marc Neuhaus * @Admin\Annotations\Navigation(title="Migration Generator", position="system:left", priority="10") * @Admin\Annotations\Access(admin="true") */ public function schemaAction($tables = array(), $package = null) { if (count($tables) > 0) { $this->doctrineService->generateAndExecutePartialMigrationFor($tables, $package); } $schemaDiff = $this->doctrineService->getDatabaseDiff(); $this->view->assign("schemaDiff", $schemaDiff); $packages = $this->packageManager->getActivePackages(); $this->view->assign("packages", $packages); }
/** * Tries to load the reflection data from the compile time cache. * * The compile time cache is only supported for Development context and thus * this function will return in any other context. * * If no reflection data was found, this method will at least load the precompiled * reflection data of any possible frozen package. Even if precompiled reflection * data could be loaded, FALSE will be returned in order to signal that other * packages still need to be reflected. * * @return boolean TRUE if reflection data could be loaded, otherwise FALSE */ protected function loadClassReflectionCompiletimeCache() { $data = $this->reflectionDataCompiletimeCache->get('ReflectionData'); if ($data === FALSE) { if ($this->context->isDevelopment()) { $useIgBinary = extension_loaded('igbinary'); foreach ($this->packageManager->getActivePackages() as $packageKey => $package) { if ($this->packageManager->isPackageFrozen($packageKey)) { $pathAndFilename = $this->getPrecompiledReflectionStoragePath() . $packageKey . '.dat'; if (file_exists($pathAndFilename)) { $data = $useIgBinary ? igbinary_unserialize(file_get_contents($pathAndFilename)) : unserialize(file_get_contents($pathAndFilename)); foreach ($data as $propertyName => $propertyValue) { $this->{$propertyName} = \TYPO3\FLOW3\Utility\Arrays::arrayMergeRecursiveOverrule($this->{$propertyName}, $propertyValue); } } } } } return FALSE; } foreach ($data as $propertyName => $propertyValue) { $this->{$propertyName} = $propertyValue; } return TRUE; }
/** * returns classes that are taged with all of the specified tags * * @param string $tags * @return void * @author Marc Neuhaus */ public function getClassesAnnotatedWith($tags) { $cache = $this->cacheManager->getCache('Admin_ImplementationCache'); $identifier = "ClassesTaggedWith-" . implode("_", $tags); if (!$cache->has($identifier)) { $classes = array(); $activePackages = $this->packageManager->getActivePackages(); foreach ($activePackages as $packageName => $package) { if (substr($packageName, 0, 8) === "Doctrine") { continue; } foreach ($package->getClassFiles() as $class => $file) { $annotations = $this->getClassConfiguration($class); $tagged = true; foreach ($tags as $tag) { if (!isset($annotations[$tag])) { $tagged = false; } } if ($tagged) { $classes[$class] = $packageName; } } } $cache->set($identifier, $classes); } elseif (isset($this->runtimeCache[$identifier])) { $classes = $this->runtimeCache[$identifier]; } else { $this->runtimeCache[$identifier] = $classes = $cache->get($identifier); } return $classes; }
/** * Action for listing active packages * * @return string */ public function listActiveAction() { $packages = $this->packageManager->getActivePackages(); $output = 'Active packages:' . PHP_EOL; foreach ($packages as $package) { $output .= ' ' . str_pad($package->getPackageKey(), 30) . $package->getPackageMetaData()->getVersion() . PHP_EOL; } return $output . PHP_EOL; }
/** * 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'); $title = $page1->createElement('connectionSection', 'TYPO3.Form:Section'); $title->setLabel('Import a site'); $sitePackages = array(); foreach ($this->packageManager->getActivePackages() as $package) { $packageMetaData = $package->getPackageMetaData(); if (in_array('Site', $packageMetaData->getCategories())) { $sitePackages[$package->getPackageKey()] = $packageMetaData->getTitle(); } } if (count($sitePackages) > 0) { $site = $title->createElement('site', 'TYPO3.Form:SingleSelectDropdown'); $site->setLabel('Select a site'); $site->setProperty('options', $sitePackages); $site->addValidator(new \TYPO3\FLOW3\Validation\Validator\NotEmptyValidator()); $sites = $this->siteRepository->findAll(); if ($sites->count() > 0) { $prune = $title->createElement('prune', 'TYPO3.Form:Checkbox'); $prune->setLabel('Delete existing sites'); } } else { $error = $title->createElement('error', 'TYPO3.Form:StaticText'); $error->setProperty('text', 'No site packages were available, make sure you have an active site package'); $error->setProperty('class', 'alert alert-warning'); } $newPackageSection = $page1->createElement('newPackageSection', 'TYPO3.Form:Section'); $newPackageSection->setLabel('Create a new site'); $packageName = $newPackageSection->createElement('packageKey', 'TYPO3.Form:SingleLineText'); $packageName->setLabel('Package Name (in form "Vendor.MyPackageName")'); $packageName->addValidator(new \TYPO3\FLOW3\Validation\Validator\RegularExpressionValidator(array('regularExpression' => \TYPO3\FLOW3\Package\PackageInterface::PATTERN_MATCH_PACKAGEKEY))); $siteName = $newPackageSection->createElement('siteName', 'TYPO3.Form:SingleLineText'); $siteName->setLabel('Site Name'); $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); }
/** * Return the configuration needed for Migrations. * * @return \Doctrine\DBAL\Migrations\Configuration\Configuration */ protected function getMigrationConfiguration() { $this->output = array(); $that = $this; $outputWriter = new \Doctrine\DBAL\Migrations\OutputWriter(function ($message) use($that) { $that->output[] = $message; }); $configuration = new \Doctrine\DBAL\Migrations\Configuration\Configuration($this->entityManager->getConnection(), $outputWriter); $configuration->setMigrationsNamespace('TYPO3\\FLOW3\\Persistence\\Doctrine\\Migrations'); $configuration->setMigrationsDirectory(\TYPO3\FLOW3\Utility\Files::concatenatePaths(array(FLOW3_PATH_DATA, 'DoctrineMigrations'))); $configuration->setMigrationsTableName('flow3_doctrine_migrationstatus'); $configuration->createMigrationTable(); $databasePlatformName = $this->getDatabasePlatformName(); foreach ($this->packageManager->getActivePackages() as $package) { $configuration->registerMigrationsFromDirectory(\TYPO3\FLOW3\Utility\Files::concatenatePaths(array($package->getPackagePath(), 'Migrations', $databasePlatformName))); } return $configuration; }
/** * Finds all Locale objects representing locales available in the * FLOW3 installation. This is done by scanning all Private and Public * resource files of all active packages, in order to find localized files. * * Localized files have a locale identifier added before their extension * (or at the end of filename, if no extension exists). For example, a * localized file for foobar.png, can be foobar.en.png, fobar.en_GB.png, etc. * * Just one localized resource file causes the corresponding locale to be * regarded as available (installed, supported). * * Note: result of this method invocation is cached * * @return void */ protected function generateAvailableLocalesCollectionByScanningFilesystem() { foreach ($this->packageManager->getActivePackages() as $activePackage) { $packageResourcesPath = $this->localeBasePath . $activePackage->getPackageKey() . '/'; if (!is_dir($packageResourcesPath)) { continue; } $directoryIterator = new \RecursiveDirectoryIterator($packageResourcesPath, \RecursiveDirectoryIterator::UNIX_PATHS); $recursiveIteratorIterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST); foreach ($recursiveIteratorIterator as $fileOrDirectory) { if ($fileOrDirectory->isFile()) { $localeIdentifier = Utility::extractLocaleTagFromFilename($fileOrDirectory->getFilename()); if ($localeIdentifier !== FALSE) { $this->localeCollection->addLocale(new Locale($localeIdentifier)); } } } } }
/** * 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.flow3:package:unfreeze * @see typo3.flow3: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 = array(); if ($packageKey === 'all') { foreach (array_keys($this->packageManager->getActivePackages()) as $packageKey) { if (!$this->packageManager->isPackageFrozen($packageKey)) { $packagesToFreeze[] = $packageKey; } } if ($packagesToFreeze === array()) { $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.', array($packageKey)); $this->quit(1); } else { $this->outputLine('Package "%s" is not available.', array($packageKey)); $this->quit(2); } } if ($this->packageManager->isPackageFrozen($packageKey)) { $this->outputLine('Package "%s" was already frozen.', array($packageKey)); $this->quit(0); } $packagesToFreeze = array($packageKey); } foreach ($packagesToFreeze as $packageKey) { $this->packageManager->freezePackage($packageKey); $this->outputLine('Froze package "%s".', array($packageKey)); } }
/** * Flush all caches * * The flush command flushes all caches (including code caches) which have been * registered with FLOW3's Cache Manager. It also removes any session data. * * If fatal errors caused by a package prevent the compile time bootstrap * from running, the removal of any temporary data can be forced by specifying * the option <b>--force</b>. * * This command does not remove the precompiled data provided by frozen * packages unless the <b>--force</b> option is used. * * @param boolean $force Force flushing of any temporary data * @return void * @see typo3.flow3:cache:warmup * @see typo3.flow3:package:freeze * @see typo3.flow3:package:refreeze */ public function flushCommand($force = FALSE) { // Internal note: the $force option is evaluated early in the FLOW3 // bootstrap in order to reliably flush the temporary data before any // other code can cause fatal errors. $currentSessionImplementation = $this->objectManager->getClassNameByObjectName('TYPO3\\FLOW3\\Session\\SessionInterface'); $result = $currentSessionImplementation::destroyAll($this->bootstrap); if ($result === NULL) { $sessionDestroyMessage = ' and removed all potentially existing session data.'; } elseif ($result > 0) { $sessionDestroyMessage = sprintf(' and removed data of %s.', $result === 1 ? 'the one existing session' : 'the ' . $result . ' existing sessions'); } else { $sessionDestroyMessage = '.'; } $this->cacheManager->flushCaches(); $this->outputLine('Flushed all caches for "' . $this->bootstrap->getContext() . '" context' . $sessionDestroyMessage); if ($this->lockManager->isSiteLocked()) { $this->lockManager->unlockSite(); } $frozenPackages = array(); foreach (array_keys($this->packageManager->getActivePackages()) as $packageKey) { if ($this->packageManager->isPackageFrozen($packageKey)) { $frozenPackages[] = $packageKey; } } if ($frozenPackages !== array()) { $this->outputFormatted(PHP_EOL . 'Please note that the following package' . (count($frozenPackages) === 1 ? ' is' : 's are') . ' currently frozen: ' . PHP_EOL); $this->outputFormatted(implode(PHP_EOL, $frozenPackages) . PHP_EOL, array(), 2); $message = 'As code and configuration changes in these packages are not detected, the application may respond '; $message .= 'unexpectedly if modifications were done anyway or the remaining code relies on these changes.' . PHP_EOL . PHP_EOL; $message .= 'You may call <b>package:refreeze all</b> in order to refresh frozen packages or use the <b>--force</b> '; $message .= 'option of this <b>cache:flush</b> command to flush caches if FLOW3 becomes unresponsive.' . PHP_EOL; $this->outputFormatted($message, array($frozenPackages)); } $this->sendAndExit(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>'); } }