/** * @throws \RuntimeException */ public function loadConfig() { if (!file_exists(OLDSTYLE_WORKING_DIR . '.Oldstyle')) { throw new \RuntimeException("Directory is not a Oldstyle directory: " . OLDSTYLE_WORKING_DIR); } $config = Oldstyle::fromYAML(file_get_contents(OLDSTYLE_WORKING_DIR . '.Oldstyle')); if (!$config || !is_array($config)) { throw new \RuntimeException("Error: " . OLDSTYLE_WORKING_DIR . ".Oldstyle file is not valid YAML, or is empty."); } $this->config = $config; return $config; }
/** * Initializes the command just after the input has been validated. * * This is mainly useful when a lot of commands extends one main command * where some things need to be initialized based on the input arguments and options. * * @param InputInterface $input An InputInterface instance * @param OutputInterface $output An OutputInterface instance */ public function initialize(InputInterface $input, OutputInterface $output) { $this->startTime = microtime(true); $this->input = $input; $this->output = $output; if ($this->loadConfig) { $this->config = Oldstyle::loadConfig(); } if ($this->loadMODX) { $this->modx = Oldstyle::loadMODX(); } }
/** * Loops over an object folder and parses the files to pass to buildSingleObject * * @param $folder * @param $type */ public function buildObjects($folder, $type) { if (!file_exists(OLDSTYLE_WORKING_DIR . $folder)) { $this->output->writeln('> Skipping ' . $type['class'] . ', ' . $folder . ' does not exist.'); return; } $criteria = $this->getPartitionCriteria($type['folder']); if (is_null($criteria)) { $criteria = array(); } if ($this->isForce) { $this->modx->removeCollection($type['class'], $criteria); if (isset($type['truncate_on_force'])) { foreach ($type['truncate_on_force'] as $class) { $this->output->writeln('> Truncating ' . $class . ' before force building ' . $type['class'] . '...'); $this->modx->removeCollection($class, array()); } } /** * @deprecated 2015-03-30 * * Deprecated in favour of specifying truncate_on_force in the .oldstyle file. */ switch ($type['class']) { // $modx->removeCollection does not automatically remove related objects, which in the case // of modCategory results in orphaned modCategoryClosure objects. Normally, this is okay, because // Oldstyle recreates the objects with the same ID. But Categories automatically add the closure on // save, which then throws a bunch of errors about duplicate IDs. Worst of all, it _removes_ the // category object if it can't save the closure, causing the IDs to go all over the place. // So in this case, we make sure all category closures are wiped too. case 'modCategory': $this->modx->removeCollection('modCategoryClosure', array()); break; } } $directory = new \DirectoryIterator(OLDSTYLE_WORKING_DIR . $folder); // Reset the conflicts so we're good to go on new ones $this->resetConflicts(); $this->getExistingObjects($type['class'], $criteria); foreach ($directory as $file) { /** @var \SplFileInfo $file */ $name = $file->getBasename(); // Ignore dotfiles/folders if (substr($name, 0, 1) == '.') { continue; } if (!$file->isFile()) { $this->output->writeln('- Skipping ' . $file->getType() . ': ' . $name); continue; } // Load the file contents $file = file_get_contents($file->getRealPath()); // Normalize line-endings to \n to ensure consistency $file = str_replace("\r\n", "\n", $file); $file = str_replace("\r", "\n", $file); // Get the raw data, and the content list($rawData, $content) = explode(Oldstyle::$contentSeparator, $file); // Turn the raw YAML data into an array $data = Oldstyle::fromYAML($rawData); if (!empty($content)) { $data['content'] = $content; } $this->buildSingleObject($data, $type); } $this->removeOrphans($type); $this->resolveConflicts($folder, $type); }
define('OLDSTYLE_WORKING_DIR', $cwd = getcwd() . DIRECTORY_SEPARATOR); } if (!defined('OLDSTYLE_APP_DIR')) { define('OLDSTYLE_APP_DIR', dirname(__FILE__)); } /** * Load all the commands and create the Oldstyle instance */ use etiqa\Oldstyle\Oldstyle; use etiqa\Oldstyle\Command\BackupCommand; use etiqa\Oldstyle\Command\BuildCommand; use etiqa\Oldstyle\Command\ExtractCommand; use etiqa\Oldstyle\Command\InitCommand; use etiqa\Oldstyle\Command\InstallModxCommand; use etiqa\Oldstyle\Command\UpgradeModxCommand; use etiqa\Oldstyle\Command\InstallPackageCommand; use etiqa\Oldstyle\Command\RestoreCommand; use etiqa\Oldstyle\Command\MigrateCommand; $application = new Oldstyle('Oldstyle', '0.2.0'); $application->add(new InitCommand()); $application->add(new InstallModxCommand()); $application->add(new UpgradeModxCommand()); $application->add(new InstallPackageCommand()); $application->add(new BackupCommand()); $application->add(new RestoreCommand()); $application->add(new MigrateCommand()); /** * We return it so the CLI controller in /Oldstyle can run it, or for other integrations to * work with the Oldstyle api directly. */ return $application;
/** * Runs the command. * * @param InputInterface $input * @param OutputInterface $output * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { /** * @var $database_type * @var $database_server * @var $database_user * @var $database_password * @var $dbase * @var */ include MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php'; if ($database_type !== 'mysql') { $output->writeln('<error>Sorry, only MySQL is supported as database driver currently.</error>'); return 1; } // Grab the directory to place the backup $backupDirectory = isset($this->config['backup_directory']) ? $this->config['backup_directory'] : '_backup/'; $targetDirectory = OLDSTYLE_WORKING_DIR . $backupDirectory; // Make sure the directory exists if (!is_dir($targetDirectory)) { mkdir($targetDirectory); if (!is_dir($targetDirectory)) { $output->writeln('<error>Could not create {$backupDirectory} folder</error>'); return 1; } } // Compute the name $file = $input->getArgument('name'); if (!empty($file)) { $file = $this->modx->filterPathSegment($file); } else { $file = strftime('%Y-%m-%d-%H%M%S-%z'); } if (substr($file, -4) != '.sql') { $file .= '.sql'; } // Full target directory and file $targetFile = $targetDirectory . $file; if (file_exists($targetFile)) { $output->writeln('<error>A file with the name {$file} already exists in {$backupDirectory}.</error>'); return 1; } $output->writeln('Writing database backup to <info>' . $file . '</info>...'); $database_password = str_replace("'", '\'', $database_password); $password_parameter = ''; if ($database_password != '') { $password_parameter = "-p'{$database_password}'"; } if ($input->getOption("exclude-tables")) { $tables = $config = Oldstyle::fromYAML(file_get_contents(OLDSTYLE_WORKING_DIR . $this->config['backup_ignore_table_file'])); $exclude_tables = ""; foreach ($tables as $table) { $exclude_tables .= " "; $exclude_tables .= "--ignore-table={$dbase}.{$table}"; } exec("mysqldump -u {$database_user} {$password_parameter} -h {$database_server} {$dbase} {$exclude_tables}> {$targetFile} "); } else { exec("mysqldump -u {$database_user} {$password_parameter} -h {$database_server} {$dbase} > {$targetFile} "); } return 0; }
/** * @param \xPDOObject|\modElement $object * @param array $options * @return string */ public function generate($object, array $options = array()) { $fieldMeta = $object->_fieldMeta; $data = $this->objectToArray($object); // If there's a dedicated content field, we put that below the yaml for easier managing, // unless the object is a modStaticResource, calling getContent on a static resource can break the // extracting because it tries to return the (possibly binary) file. // the same problem with modDashboardWidget, it's have custom getContent method $content = ''; if (method_exists($object, 'getContent') && !$object instanceof \modStaticResource && !$object instanceof \modDashboardWidget) { $content = $object->getContent(); if (!empty($content)) { foreach ($data as $key => $value) { if ($value === $content) { unset($data[$key]); } } } } // Strip out keys that have the same value as the default, or are excluded per the .oldstyle $excludes = isset($options['exclude_keys']) && is_array($options['exclude_keys']) ? $options['exclude_keys'] : array(); foreach ($data as $key => $value) { if (isset($fieldMeta[$key]['default']) && $value === $fieldMeta[$key]['default'] || in_array($key, $excludes)) { unset($data[$key]); } } $out = Oldstyle::toYAML($data); if (!empty($content)) { $out .= Oldstyle::$contentSeparator . $content; } return $out; }
/** * Runs the command. * * @param InputInterface $input * @param OutputInterface $output * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { // Make sure we're not overwriting existing configuration by checking for existing .oldstyle files if (file_exists(OLDSTYLE_WORKING_DIR . '.oldstyle')) { // If the overwrite option is set we'll warn the user but continue anyway if ($input->getOption('overwrite')) { $output->writeln('<comment>A Oldstyle project already exists in this directory. If you continue, this will be overwritten.</comment>'); } else { $output->writeln('<error>Error: a Oldstyle project already exists in this directory.</error> If you wish to continue anyway, specify the --overwrite flag.'); return 1; } } $helper = $this->getHelper('question'); // Where we'll store the configuration $data = array(); /** * Ask the user for the data directory to store object files in */ $question = new Question('Please enter the name of the data directory (defaults to _data/): ', '_data'); $directory = $helper->ask($input, $output, $question); if (empty($directory)) { $directory = '_data/'; } $directory = trim($directory, '/') . '/'; $data['data_directory'] = $directory; if (!file_exists($directory)) { mkdir($directory); } /** * Ask the user for a backup directory to store database backups in */ $question = new Question('Please enter the name of the backup directory (defaults to _backup/): ', '_backup'); $directory = $helper->ask($input, $output, $question); if (empty($directory)) { $directory = '_backup/'; } $directory = trim($directory, '/') . '/'; $data['backup_directory'] = $directory; if (!file_exists($directory)) { mkdir($directory); } /** * Ask the user for a migrate directory used to store migrate sql */ $question = new Question('Please enter the name of the migrate directory (defaults to _migrate/): ', '_migrate'); $directory = $helper->ask($input, $output, $question); if (empty($directory)) { $directory = '_migrate/'; } $directory = trim($directory, '/') . '/'; $data['migrate_directory'] = $directory; if (!file_exists($directory)) { mkdir($directory); } $question = new Question('Please enter the name the file you want to use to ignore tables in backup (defaults to .backup_ignore_tables): ', '.backup_ignore_tables'); $ignore_file = $helper->ask($input, $output, $question); $data['backup_ignore_table_file'] = $ignore_file; if (!file_exists($ignore_file)) { fopen($ignore_file, "w"); } /** * Ask if we want to include some default data types */ if (file_exists(OLDSTYLE_WORKING_DIR . 'config.core.php')) { $question = new ConfirmationQuestion('Would you like to include a list of <info>Currently Installed Packages</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $modx = false; try { $modx = Oldstyle::loadMODX(); } catch (\RuntimeException $e) { $output->writeln('<error>Could not get a list of packages because MODX could not be loaded: ' . $e->getMessage() . '</error>'); } if ($modx) { $providers = array(); foreach ($modx->getIterator('transport.modTransportProvider') as $provider) { /** @var \modTransportProvider $provider */ $name = $provider->get('name'); $providers[$name] = array('service_url' => $provider->get('service_url')); if ($provider->get('description')) { $providers[$name]['description'] = $provider->get('description'); } if ($provider->get('username')) { $providers[$name]['username'] = $provider->get('username'); } if ($provider->get('api_key')) { $key = $provider->get('api_key'); file_put_contents(OLDSTYLE_WORKING_DIR . '.' . $name . '.key', $key); $providers[$name]['api_key'] = '.' . $name . '.key'; } $c = $modx->newQuery('transport.modTransportPackage'); $c->where(array('provider' => $provider->get('id'))); $c->groupby('package_name'); foreach ($modx->getIterator('transport.modTransportPackage', $c) as $package) { $packageName = $package->get('signature'); $providers[$name]['packages'][] = $packageName; } } $data['packages'] = $providers; } } } /** * Turn the configuration into YAML, and write the file. */ $config = Oldstyle::toYAML($data); file_put_contents(OLDSTYLE_WORKING_DIR . '.oldstyle', $config); $output->writeln('<info>Oldstyle Project initiated and .oldstyle file written.</info>'); /** * Check if we already have MODX installed, and if not, offer to install it right away. */ if (!file_exists(OLDSTYLE_WORKING_DIR . 'config.core.php')) { $question = new ConfirmationQuestion('No MODX installation found in the current directory. Would you like to install the latest stable version? <comment>(Y/N)</comment> ', false); if ($helper->ask($input, $output, $question)) { $command = $this->getApplication()->find('modx:install'); $arguments = array('command' => 'modx:install'); $input = new ArrayInput($arguments); return $command->run($input, $output); } } return 0; }