/** * @throws \RuntimeException */ public static function loadConfig() { if (!file_exists(GITIFY_WORKING_DIR . '.gitify')) { throw new \RuntimeException("Directory is not a Gitify directory: " . GITIFY_WORKING_DIR); } $config = Gitify::fromYAML(file_get_contents(GITIFY_WORKING_DIR . '.gitify')); if (!$config || !is_array($config)) { throw new \RuntimeException("Error: " . GITIFY_WORKING_DIR . ".gitify file is not valid YAML, or is empty."); } 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 = Gitify::loadConfig(); } if ($this->loadMODX) { $this->modx = Gitify::loadMODX(); } }
/** * Runs the command. * * @param InputInterface $input * @param OutputInterface $output * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { $this->modx->setLogTarget('ECHO'); $this->modx->setLogLevel(\modX::LOG_LEVEL_INFO); if ($input->getOption('all')) { // check list and run install for each $packages = isset($this->config['packages']) ? $this->config['packages'] : array(); foreach ($packages as $provider_name => $provider_data) { // Try to load the provider from the database $provider = $this->modx->getObject('transport.modTransportProvider', array("name" => $provider_name)); // If no provider found, then we'll create it if (!$provider) { $credentials = array('username' => isset($provider_data['username']) ? $provider_data['username'] : '', 'api_key' => ''); // Try to look for a file with the API Key from a file within the gitify working directory if (!empty($provider_data['api_key']) && file_exists(GITIFY_WORKING_DIR . '/' . $provider_data['api_key'])) { $credentials['api_key'] = trim(file_get_contents(GITIFY_WORKING_DIR . '/' . $provider_data['api_key'])); } // load provider credentials from file if (!empty($provider_data['credential_file']) && file_exists(GITIFY_WORKING_DIR . '/' . $provider_data['credential_file'])) { $credentials_content = trim(file_get_contents(GITIFY_WORKING_DIR . '/' . $provider_data['credential_file'])); $credentials = Gitify::fromYAML($credentials_content); } /** @var \modTransportProvider $provider */ $provider = $this->modx->newObject('transport.modTransportProvider'); $provider->fromArray(array('name' => $provider_name, 'service_url' => $provider_data['service_url'], 'description' => isset($provider_data['description']) ? $provider_data['description'] : '', 'username' => $credentials['username'], 'api_key' => $credentials['api_key'])); $provider->save(); } foreach ($provider_data['packages'] as $package) { if (!$input->getOption('interactive')) { $this->setInteractive(false); } $this->install($package, $provider); } } $this->output->writeln("<info>Done!</info>"); return 0; } // install defined package $this->install($this->input->getArgument('package_name')); 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, $options); // 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 .gitify $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 = Gitify::toYAML($data); if (!empty($content)) { $out .= Gitify::$contentSeparator . $content; } return $out; }
} /** * Specify the working directory, if it hasn't been set yet. */ if (!defined('GITIFY_WORKING_DIR')) { define('GITIFY_WORKING_DIR', $cwd = getcwd() . DIRECTORY_SEPARATOR); } /** * Load all the commands and create the Gitify instance */ use modmore\Gitify\Gitify; use modmore\Gitify\Command\BackupCommand; use modmore\Gitify\Command\BuildCommand; use modmore\Gitify\Command\ExtractCommand; use modmore\Gitify\Command\InitCommand; use modmore\Gitify\Command\InstallModxCommand; use modmore\Gitify\Command\InstallPackageCommand; use modmore\Gitify\Command\RestoreCommand; $application = new Gitify('Gitify', '0.9.0'); $application->add(new InitCommand()); $application->add(new BuildCommand()); $application->add(new ExtractCommand()); $application->add(new InstallModxCommand()); $application->add(new InstallPackageCommand()); $application->add(new BackupCommand()); $application->add(new RestoreCommand()); /** * We return it so the CLI controller in /Gitify can run it, or for other integrations to * work with the Gitify api directly. */ return $application;
/** * 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(GITIFY_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 .gitify 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 // Gitify 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(GITIFY_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(Gitify::$contentSeparator, $file); // Turn the raw YAML data into an array $data = Gitify::fromYAML($rawData); if (!empty($content)) { $data['content'] = $content; } $this->buildSingleObject($data, $type); } $this->removeOrphans($type); $this->resolveConflicts($folder, $type); }
/** * 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 .gitify files if (file_exists(GITIFY_WORKING_DIR . '.gitify')) { // If the overwrite option is set we'll warn the user but continue anyway if ($input->getOption('overwrite')) { $output->writeln('<comment>A Gitify project already exists in this directory. If you continue, this will be overwritten.</comment>'); } else { $output->writeln('<error>Error: a Gitify 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 if we want to include some default data types */ $dataTypes = array(); $question = new ConfirmationQuestion('Would you like to include <info>Contexts</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['contexts'] = array('class' => 'modContext', 'primary' => 'key'); $dataTypes['context_settings'] = array('class' => 'modContextSetting', 'primary' => array('context_key', 'key')); } $question = new ConfirmationQuestion('Would you like to include <info>Template Variables</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['template_variables'] = array('class' => 'modTemplateVar', 'primary' => 'name'); $dataTypes['template_variables_access'] = array('class' => 'modTemplateVarTemplate', 'primary' => array('tmplvarid', 'templateid')); } $question = new ConfirmationQuestion('Would you like to include <info>Content</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['content'] = array('type' => 'content', 'exclude_keys' => array('editedby', 'editedon'), 'truncate_on_force' => array('modTemplateVarResource')); } $question = new ConfirmationQuestion('Would you like to include <info>Categories</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['categories'] = array('class' => 'modCategory', 'primary' => 'category', 'truncate_on_force' => array('modCategoryClosure')); } $question = new ConfirmationQuestion('Would you like to include <info>Templates</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['templates'] = array('class' => 'modTemplate', 'primary' => 'templatename', 'extension' => '.html'); } $question = new ConfirmationQuestion('Would you like to include <info>Chunks</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['chunks'] = array('class' => 'modChunk', 'primary' => 'name', 'extension' => '.html'); } $question = new ConfirmationQuestion('Would you like to include <info>Snippets</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['snippets'] = array('class' => 'modSnippet', 'primary' => 'name', 'extension' => '.php'); } $question = new ConfirmationQuestion('Would you like to include <info>Plugins</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['plugins'] = array('class' => 'modPlugin', 'primary' => 'name', 'extension' => '.php'); $dataTypes['plugin_events'] = array('class' => 'modPluginEvent', 'primary' => array('pluginid', 'event')); $dataTypes['events'] = array('class' => 'modEvent', 'primary' => 'name'); } $question = new ConfirmationQuestion('Would you like to include <info>Namespaces</info>, <info>Extension Packages</info> and <info>System Settings</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['namespaces'] = array('class' => 'modNamespace', 'primary' => 'name'); $dataTypes['system_settings'] = array('class' => 'modSystemSetting', 'primary' => 'key', 'exclude_keys' => array('editedon')); $dataTypes['extension_packages'] = array('class' => 'modExtensionPackage', 'primary' => 'namespace', 'exclude_keys' => array('created_at', 'updated_at')); } $question = new ConfirmationQuestion('Would you like to include <info>Form Customization</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['fc_sets'] = array('class' => 'modFormCustomizationSet', 'primary' => 'id'); $dataTypes['fc_profiles'] = array('class' => 'modFormCustomizationProfile', 'primary' => 'id'); $dataTypes['fc_profile_usergroups'] = array('class' => 'modFormCustomizationProfileUserGroup', 'primary' => array('usergroup', 'profile')); $dataTypes['fc_action_dom'] = array('class' => 'modActionDom', 'primary' => array('set', 'name')); } $question = new ConfirmationQuestion('Would you like to include <info>Media Sources</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['mediasources'] = array('class' => 'modMediaSource', 'primary' => 'id'); $dataTypes['mediasource_elements'] = array('class' => 'sources.modMediaSourceElement', 'primary' => array('source', 'object_class', 'object', 'context_key')); } $question = new ConfirmationQuestion('Would you like to include <info>Dashboards</info>? <comment>(Y/N)</comment> ', true); if ($helper->ask($input, $output, $question)) { $dataTypes['dashboards'] = array('class' => 'modDashboard', 'primary' => array('id', 'name')); $dataTypes['dashboard_widgets'] = array('class' => 'modDashboardWidget', 'primary' => 'id'); $dataTypes['dashboard_widget_placement'] = array('class' => 'modDashboardWidgetPlacement', 'primary' => array('dashboard', 'widget')); } $data['data'] = $dataTypes; if (file_exists(GITIFY_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 = Gitify::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(GITIFY_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 = Gitify::toYAML($data); file_put_contents(GITIFY_WORKING_DIR . '.gitify', $config); $output->writeln('<info>Gitify Project initiated and .gitify file written.</info>'); /** * Check if we already have MODX installed, and if not, offer to install it right away. */ if (!file_exists(GITIFY_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; }
private function savePackageToFile($packageSignature, $provider) { $packages = isset($this->config['packages']) ? $this->config['packages'] : array(); $name = $provider->get('name'); if ($packages && array_key_exists($name, $packages)) { if (in_array($packageSignature, $packages[$name])) { return false; } } $configProvider = isset($packages[$name]) ? $packages[$name] : array(); if (!$configProvider) { /** @var \modTransportProvider $provider */ $configProvider = array('service_url' => $provider->get('service_url')); if ($provider->get('description')) { $configProvider['description'] = $provider->get('description'); } if ($provider->get('username')) { $configProvider['username'] = $provider->get('username'); } if ($provider->get('api_key')) { $key = $provider->get('api_key'); file_put_contents(GITIFY_WORKING_DIR . '.' . $name . '.key', $key); $configProvider['api_key'] = '.' . $name . '.key'; } } $configProvider['packages'][] = $packageSignature; $packages[$name] = $configProvider; $this->config['packages'] = $packages; $config = Gitify::toYAML($this->config); file_put_contents(GITIFY_WORKING_DIR . '.gitify', $config); $this->output->writeln("<info>Add {$packageSignature} to .gitify</info>"); return true; }