/** * List an organization's sites * * ## OPTIONS * * <add|remove|list> * : subfunction to run * * [--org=<id|name>] * : Organization UUID or name * * [--tag=<tag>] * : Tag name to filter sites list by * * [--site=<site>] * : Site to add to or remove from organization * * @subcommand sites * */ public function sites($args, $assoc_args) { $action = array_shift($args); $org_id = Input::orgid($assoc_args, 'org', null, array('allow_none' => false)); $orgs = new UserOrganizationMemberships(); $org = $orgs->get($org_id); $org_info = $org->get('organization'); $memberships = $org->site_memberships->all(); switch ($action) { case 'add': if (isset($assoc_args['site'])) { if ($this->siteIsMember($memberships, $assoc_args['site'])) { $this->failure('{site} is already a member of {org}', array('site' => $assoc_args['site'], 'org' => $org_info->profile->name)); } else { $site = $this->sites->get($assoc_args['site']); } } else { $site = $this->sites->get(Input::menu($this->getNonmemberSiteList($memberships), null, 'Choose site')); } Terminus::confirm('Are you sure you want to add %s to %s ?', array($site->get('name'), $org_info->profile->name)); $workflow = $org->site_memberships->addMember($site); $workflow->wait(); $this->workflowOutput($workflow); break; case 'remove': if (isset($assoc_args['site'])) { if (!$this->siteIsMember($memberships, $assoc_args['site'])) { $this->failure('{site} is not a member of {org}', array('site' => $assoc_args['site'], 'org' => $org_info->profile->name)); } else { $site = $this->sites->get($assoc_args['site']); } } else { $site = $this->sites->get(Input::menu($this->getMemberSiteList($memberships), null, 'Choose site')); } $member = $org->site_memberships->get($site->get('id')); Terminus::confirm('Are you sure you want to remove %s from %s ?', array($site->get('name'), $org_info->profile->name)); $workflow = $member->removeMember(); $workflow->wait(); $this->workflowOutput($workflow); break; case 'list': default: foreach ($memberships as $membership) { if (isset($assoc_args['tag']) && !in_array($assoc_args['tag'], $membership->get('tags'))) { continue; } $site = $membership->get('site'); $data_array = array('name' => null, 'id' => null, 'service_level' => null, 'framework' => null, 'created' => null, 'tags' => $membership->get('tags')); foreach ($data_array as $key => $value) { if ($value == null && isset($site->{$key})) { $data_array[$key] = $site->{$key}; } } $data_array['created'] = date('Y-m-dTH:i:s', $data_array['created']); $data[] = $data_array; } $this->output()->outputRecordList($data); break; } }
/** * Helper function to get role * * @param [array] $assoc_args Argument array passed from commands * @param [string] $message Prompt to STDOUT * @return [string] $role Name of role */ public static function role($assoc_args, $message = 'Select a role for this member') { $roles = array('developer', 'team_member', 'admin'); if (!isset($assoc_args['role']) || !in_array(strtolower($assoc_args['role']), $roles)) { $role = strtolower($roles[Input::menu($roles, null, 'Select a role for the new member')]); } else { $role = $assoc_args['role']; } return $role; }
/** * Manage site organization tags * * ## OPTIONS * * <add|remove|list> * : subfunction to run * * [--site=<site>] * : Site's name * * [--org=<name|id>] * : Organization to apply tag with * * [--tag=<tag>] * : Tag to add or remove * * @subcommand tags */ public function tags($args, $assoc_args) { $action = array_shift($args); $site = $this->sites->get(Input::sitename($assoc_args)); $org = Input::orgid($assoc_args, 'org'); if ($site->organizationIsMember($org)) { switch ($action) { case 'add': $tag = Input::string($assoc_args, 'tag', 'Enter a tag to add'); $response = $site->addTag($tag, $org); $context = array('tag' => $tag, 'site' => $site->get('name')); if ($response['status_code'] == 200) { $this->log()->info('Tag "{tag}" has been added to {site}', $context); } else { $this->failure('Tag "{tag}" could not be added to {site}', $context); } break; case 'remove': $tags = $site->getTags($org); if (count($tags) === 0) { $message = 'This organization does not have any tags associated'; $message .= ' with this site.'; $this->failure($message); } elseif (!isset($assoc_args['tag']) || !in_array($assoc_args['tag'], $tags)) { $tag = $tags[Input::menu($tags, null, 'Select a tag to delete')]; } else { $tag = $assoc_args['tag']; } $response = $site->removeTag($tag, $org); $context = array('tag' => $tag, 'site' => $site->get('name')); if ($response['status_code'] == 200) { $this->log()->info('Tag "{tag}" has been removed from {site}', $context); } else { $this->failure('Tag "{tag}" could not be removed from {site}', $context); } break; case 'list': default: $tags = $site->getTags($org); $this->output()->outputRecord(compact('tags')); break; } } else { $message = '{site} is not a member of an organization,'; $message .= ' which is necessary to associate a tag with a site.'; $this->failure($message, array('site' => $site->get('name'))); } }
/** * Get, load, create, or list backup information * * ## OPTIONS * * <get|load|create|list> * : function to run - get, load, create, or list * * [--site=<site>] * : Site to load * * [--env=<env>] * : Environment to load * * [--element=<code|files|db|all>] * : Element to download or create. *all* only used for the 'create' * * [--to-directory=<directory>] * : Download the file if set * * [--latest] * : if set no the latest backup will be selected automatically * * [--keep-for] * : number of days to keep this backup. * * @subcommand backup * */ public function backup($args, $assoc_args) { $action = array_shift($args); $site = SiteFactory::instance(Input::site($assoc_args)); $env = Input::env($assoc_args, 'env'); switch ($action) { case 'get': // prompt for backup type if (!($element = @$assoc_args['element'])) { $element = Terminus::menu(array('code', 'files', 'database'), null, "Select type backup", TRUE); } if (!in_array($element, array('code', 'files', 'database'))) { Terminus::error("Invalid backup element specified."); } $latest = Input::optional('latest', $assoc_args, false); $backups = $site->environment($env)->backups($element, $latest); if (empty($backups)) { \Terminus::error('No backups available.'); } $menu = $folders = array(); // build a menu for selecting back ups foreach ($backups as $folder => $backup) { if (!isset($backup->filename)) { continue; } if (!isset($backup->folder)) { $backup->folder = $folder; } $buckets[] = $backup->folder; $menu[] = $backup->filename; } if (empty($menu)) { Terminus::error("No backups available. Create one with `terminus site backup create --site=%s --env=%s`", array($site->getName(), $env)); } $index = 0; if (!$latest) { $index = Terminus::menu($menu, null, "Select backup"); } $bucket = $buckets[$index]; $filename = $menu[$index]; $url = $site->environment($env)->backupUrl($bucket, $element); if (isset($assoc_args['to-directory'])) { Terminus::line("Downloading ... please wait ..."); $filename = \Terminus\Utils\get_filename_from_url($url->url); $target = sprintf("%s/%s", $assoc_args['to-directory'], $filename); if (Terminus_Command::download($url->url, $target)) { Terminus::success("Downloaded %s", $target); return $target; } else { Terminus::error("Could not download file"); } } echo $url->url; return $url->url; break; case 'load': $assoc_args['to-directory'] = '/tmp'; $assoc_args['element'] = 'database'; $database = @$assoc_args['database'] ?: false; $username = @$assoc_args['username'] ?: false; $password = @$assoc_args['password'] ?: false; exec("mysql -e 'show databases'", $stdout, $exit); if (0 != $exit) { Terminus::error("MySQL does not appear to be installed on your server."); } $assoc_args['env'] = $env; $target = $this->backup(array('get'), $assoc_args); $target = \Terminus\Utils\get_filename_from_url($target); $target = "/tmp/{$target}"; if (!file_exists($target)) { Terminus::error("Can't read database file %s", array($target)); } Terminus::line("Unziping database"); exec("gunzip {$target}", $stdout, $exit); // trim the gz of the target $target = Terminus\Utils\sql_from_zip($target); $target = escapeshellarg($target); if (!$database) { $database = escapeshellarg(Terminus::prompt("Name of database to import to")); } if (!$username) { $username = escapeshellarg(Terminus::prompt("Username")); } if (!$password) { $password = escapeshellarg(Terminus::prompt("Password")); } exec("mysql {$database} -u {$username} -p'{$password}' < {$target}", $stdout, $exit); if (0 != $exit) { Terminus::error("Could not import database"); } Terminus::success("%s successfuly imported to %s", array($target, $database)); return true; break; case 'create': if (!array_key_exists('element', $assoc_args)) { $assoc_args['element'] = Input::menu(array('code', 'db', 'files', 'all'), 'all', "Select element"); } $result = $site->environment($env)->createBackup($assoc_args); if ($result) { Terminus::success("Created backup"); } else { Terminus::error("Couldn't create backup."); } break; case 'list': case 'default': $backups = $site->environment($env)->backups(); $element = @$assoc_args['element']; $data = array(); foreach ($backups as $id => $backup) { if (!isset($backup->filename)) { continue; } $data[] = array($backup->filename, sprintf("%dMB", $backup->size / 1024 / 1024), date("Y-m-d H:i:s", $backup->finish_time)); } if (empty($backups)) { \Terminus::error("No backups found."); return false; } else { //munging data $this->handleDisplay($data, $args, array('File', 'Size', 'Date')); return $data; } break; } }
/** * Import a zip archive == see this article for more info: * http://helpdesk.getpantheon.com/customer/portal/articles/1458058-importing-a-wordpress-site * * ## OPTIONS * * [--site=<site>] * : Site to use * * [--url=<url>] * : URL of archive to import * * [--element=<element>] * : Site element to import (i.e. code, files, db, or all) * * @subcommand import */ public function import($args, $assoc_args) { $site = SiteFactory::instance(Input::site($assoc_args)); $url = Input::string($assoc_args, 'url', "URL of archive to import"); if (!$url) { Terminus::error("Please enter a URL."); } if (!isset($assoc_args['element'])) { $element_options = array('code', 'database', 'files', 'all'); $element_key = Input::menu($element_options, 'all', 'Which element are you importing?'); $element = $element_options[$element_key]; } else { $element = $assoc_args['element']; } $import = $site->import($url, $element); if ($import) { Terminus::line('Import started, you can now safely kill this script without interfering.'); $result = $this->waitOnWorkflow('sites', $site->getId(), $import->id); if ($result->result !== 'succeeded') { Terminus::error($result->reason); } else { Terminus::success("Import complete"); } } }
function testMenuSingleOptionReturningIndex() { $only_option_index = Input::menu(array('choices' => array('Pick me!'))); $this->assertInternalType('integer', $only_option_index); $this->assertEquals(0, $only_option_index); }
/** * Manage site organization tags * * ## OPTIONS * * <add|remove> * : subfunction to run * * [--site=<site>] * : Site's name * * [--org=<name|id>] * : Organization to apply tag with * * [--tag=<tag>] * : Tag to add or remove * * @subcommand tags */ public function tags($args, $assoc_args) { $action = array_shift($args); $site = SiteFactory::instance(Input::sitename($assoc_args)); $org = Input::orgid($assoc_args, 'org'); if ($site->organizationIsMember($org)) { $data = array(); switch ($action) { case 'add': $verb = 'added to'; $tag = Input::string($assoc_args, 'tag', 'Enter a tag to add'); $response = $site->addTag($tag, $org); break; case 'remove': $verb = 'removed from'; $tags = $site->getTags($org); if (count($tags) === 0) { Terminus::error('This organization does not have any tags' . ' associated with this site.'); } elseif (!isset($assoc_args['tag']) || !in_array($assoc_args['tag'], $tags)) { $tag = $tags[Input::menu($tags, null, 'Select a tag to delete')]; } else { $tag = $assoc_args['tag']; } $response = $site->removeTag($tag, $org); break; } $message = 'Tag %s %s %s %s'; $messages = array('success' => sprintf($message, '"' . $tag . '"', 'has been', $verb, $site->getName()), 'failure' => sprintf($message, '"' . $tag . '"', 'could not be', $verb, $site->getName())); $this->responseOutput($response, $messages); } else { Terminus::error($site->getName() . ' is not a member of an organization, ' . 'which is necessary to associate a tag with a site.'); } }
/** * Helper function to select Site Workflow * * @param [array<Workflow>] $workflows Array of workflows to list * @param [array] $args Args to parse value from * @param [string] $key Index to search for in args * * @return [Workflow] $workflow */ public static function workflow($workflows, $args, $key = 'workflow_id') { if (isset($args['workflow_id'])) { $workflow_id = $args[$key]; } else { $workflow_menu_args = array(); foreach ($workflows as $workflow) { if ($workflow->get('environment')) { $environment = $workflow->get('environment'); } else { $environment = 'None'; } $created_at = date('Y-m-d H:i:s', $workflow->get('created_at')); $workflow_description = sprintf("%s - %s - %s - %s", $environment, $workflow->get('description'), $created_at, $workflow->id); $workflow_menu_args[$workflow->id] = $workflow_description; } $workflow_id = Input::menu($workflow_menu_args, null, 'Choose workflow'); } $filtered_workflow = array_filter($workflows, function ($workflow) use($workflow_id) { return $workflow->id == $workflow_id; }); if (count($filtered_workflow) > 0) { $workflow = array_values($filtered_workflow)[0]; return $workflow; } else { throw new TerminusException('Could not find workflow "{id}"', array('id' => $id), 1); } }
/** * Helper function to select Site Workflow * * @param [Site] $site Site from which to fetch workflows * @param [array] $args Args to parse value from * @param [string] $key Index to search for in args * * @return [Workflow] $workflow */ public static function workflow($site, $args, $key = 'workflow_id') { if (isset($args['workflow_id'])) { $workflow_id = $args[$key]; } else { // Only retrieve the most-recent 100 workflows $site->workflows->fetch(array('paged' => false)); $workflow_menu_args = array(); foreach ($site->workflows->all() as $workflow) { if ($workflow->get('environment')) { $environment = $workflow->get('environment'); } else { $environment = 'None'; } $created_at = date('Y-m-d H:i:s', $workflow->get('created_at')); $workflow_description = sprintf("%s - %s - %s - %s", $environment, $workflow->get('description'), $created_at, $workflow->id); $workflow_menu_args[$workflow->id] = $workflow_description; } $workflow_id = Input::menu($workflow_menu_args, null, 'Choose workflow'); } $workflow = $site->workflows->get($workflow_id); return $workflow; }
/** * Change the site payment instrument * * ## OPTIONS * * [--site=<site>] * : Site to use * * [--instrument=<UUID>] * : Change the instrument by setting the ID * * ## EXAMPLES * * terminus site instrument --site=sitename */ public function instrument($args, $assoc_args) { $user = new User(); $instruments = $user->instruments()->all(); foreach ($instruments as $instrument) { $data[$instrument->get('id')] = $instrument->get('label'); } //If site is not set, show all user's payment instruments if (!isset($assoc_args['site'])) { $this->handleDisplay($data, array(), array('UUID', 'Label')); } else { array_unshift($data, 'none'); $site = SiteFactory::instance($assoc_args['site']); //If instrument is not present, show the site's current instrument if (!isset($assoc_args['instrument'])) { $instrument_uuid = $site->get('instrument'); if ($instrument_uuid == null) { \Terminus::line($site->get('name') . ' does not have an attached payment instrument.'); } else { \Terminus::line($site->get('name') . ' is being charged to ' . $data[$instrument_uuid] . ', UUID: ' . $instrument_uuid); } } else { //Both are present. Ensure sure UUID is valid. //This attempts to prevent users from selecting instruments which do not belong to them. $instrument_id = $assoc_args['instrument']; if (!isset($data[$instrument_id])) { $location = array_search($instrument_id, $data); if ($location !== false) { $instrument_id = $location; } else { $uuids = array_keys($data); $instrument_id = Input::menu($data, null, 'Select a payment instrument'); } } //Change the instrument once we have a valid instrument. if ($instrument_id == 0) { $workflow = $site->removeInstrument(); } else { $workflow = $site->addInstrument($instrument_id); } $workflow->wait(); \Terminus::line("Successfully updated payment instrument to {$instrument_id}."); } } }
/** * List an organization's sites * * ## OPTIONS * * <add|remove|list> * : subfunction to run * * [--org=<id|name>] * : Organization UUID or name * * [--tag=<tag>] * : Tag name to filter sites list by * * [--site=<site>] * : Site to add to or remove from organization * * @subcommand sites */ public function sites($args, $assoc_args) { $action = array_shift($args); $org_id = Input::orgId(array('args' => $assoc_args, 'allow_none' => false)); // TODO: clarify that these are OrganizationMemberships, not Organization models $orgs = new UserOrganizationMemberships(); $org = $orgs->get($org_id); $org_info = $org->get('organization'); $org_model = new Organization($org_info); $memberships = $org->site_memberships->all(); switch ($action) { case 'add': if (isset($assoc_args['site'])) { if ($this->siteIsMember($memberships, $assoc_args['site'])) { $this->failure('{site} is already a member of {org}', array('site' => $assoc_args['site'], 'org' => $org_info->profile->name)); } else { $site = $this->sites->get($assoc_args['site']); } } else { $site = $this->sites->get(Input::menu(array('choices' => $this->getNonmemberSiteList($memberships), 'message' => 'Choose site'))); } Input::confirm(array('message' => 'Are you sure you want to add %s to %s ?', 'context' => array($site->get('name'), $org_info->profile->name))); $workflow = $org_model->site_memberships->addMember($site); $workflow->wait(); $this->workflowOutput($workflow); break; case 'remove': if (isset($assoc_args['site'])) { if (!$this->siteIsMember($memberships, $assoc_args['site'])) { $this->failure('{site} is not a member of {org}', array('site' => $assoc_args['site'], 'org' => $org_info->profile->name)); } else { $site = $this->sites->get($assoc_args['site']); } } else { $site = $this->sites->get(Input::menu(array('choices' => $this->getMemberSiteList($memberships), 'message' => 'Choose site'))); } $member = $org_model->site_memberships->get($site->get('id')); Input::confirm(array('message' => 'Are you sure you want to remove %s from %s ?', 'context' => array($site->get('name'), $org_info->profile->name))); $workflow = $member->removeMember(); $workflow->wait(); $this->workflowOutput($workflow); break; case 'list': default: $data = []; foreach ($memberships as $membership) { if (isset($assoc_args['tag']) && !in_array($assoc_args['tag'], $membership->get('tags'))) { continue; } $site = $membership->get('site'); $data_array = array('name' => null, 'id' => null, 'service_level' => null, 'framework' => null, 'created' => null, 'tags' => $membership->get('tags')); foreach ($data_array as $key => $value) { if ($value == null && isset($site->{$key})) { $data_array[$key] = $site->{$key}; } } $data_array['created'] = date('Y-m-dTH:i:s', strtotime($data_array['created'])); $data[] = $data_array; } if (empty($data)) { $message = 'No sites match your '; if (empty($assoc_args) || count($assoc_args) == 1 && isset($assoc_args['org'])) { $message .= 'criterion.'; } else { $message .= 'criteria.'; } $this->log()->info($message); } $this->output()->outputRecordList($data); break; } }
/** * Creates a backup * * @params [array] $assoc_args Parameters and flags from the command line * @return [Workflow[ $workflow */ private function createBackup($assoc_args) { $site = $this->sites->get(Input::sitename($assoc_args)); $env = $site->environments->get(Input::env(array('args' => $assoc_args, 'site' => $site))); if (!array_key_exists('element', $assoc_args)) { $options = array('code', 'db', 'files', 'all'); $assoc_args['element'] = $options[Input::menu($options, 'all', 'Select element')]; } $workflow = $env->createBackup($assoc_args); return $workflow; }